单链表的定义
单链表是一种顺序存储的数据结构,链表由一系列的结点组成,每个节点包含两部分,一个是存储数据元素的数据域,另一个是存储下一个节点的指针域。Golang官方包中并无链表这种数据结构,需要通过结构体模拟
type Value interface {}
type Node struct {
Data Value // 定义数据域
Next *Node // 定义值域
}
// 单向链表
type List struct {
HeadNode *Node // 头节点
TailNode *Node // 尾结点
Size int // 链表的大小
Lock *sync.Mutex // 互斥锁
}
数组和链表的区别需要从存储、查询、插入和删除几个维度来进行定义
存储
数组的存储是必须有提前申明的空间,因此数组的存储是在连续的内存地址中。 链表存储在不连续的内存空间中,从第一个空间开始,链表会记录下下一个空间的位置,这样能检索整条链表了。
查询
根据数组的特有属性,只要找到第一个元素就能知道元素的地址,理论上,get(0)和get(10000)所有的时间是一样的。而链表每个元素只知道自己上一个和下一个元素的地址(双向链表),所以得一个一个的查找。
插入和删除
数组插入一个数据比较复杂,先要判断数组空间还能不能放的下,有足够的空间将需要插入位置后面的数组整体向后移动,最后把这个数组插入。
a := make([]int, 2)
a[3] = 2
// panic: runtime error: index out of range
这里就需要使用append
a := make([]int, 2)
a[0] = 1
a[1] = 2
a = append(a, 2)
append并不是给原数组扩容,而是实例化了一个新的数组,并给新数组分配了一段新的内存空间,所以数组扩容空间复杂度比链表高。
数组查询时间复杂度O(1)修改的时间复杂度O(n) 链表的查询时间复杂度O(n)修改的时间复杂度O(1)
代码实现
package main
import (
"fmt"
"sync"
)
type Value interface {}
type Node struct {
Data Value // 定义数据域
Next *Node // 定义值域
}
type List struct {
HeadNode *Node
TailNode *Node
Size int
Lock *sync.Mutex
}
// 判断链表是否为空
func (l *List) IsEmpty() bool {
if l.HeadNode == nil {
return true
} else {
return false
}
}
// 将链表进行扩容,从尾部添加
func (l *List) Append(value Value) *Node {
node := &Node{
Data: value,
}
l.Lock.Lock()
defer l.Lock.Unlock()
if l.IsEmpty() {
l.HeadNode = node
l.TailNode = node
} else {
l.TailNode.Next = node
l.TailNode = node
}
l.Size += 1 // 链表长度加1
return node
}
// 向链表的头部添加元素
func (l *List) Add(value Value) *Node {
node := &Node{
Data: value,
Next: l.HeadNode,
}
l.Lock.Lock()
defer l.Lock.Unlock()
if l.IsEmpty() {
l.HeadNode = node
l.TailNode = node
} else {
l.HeadNode = node
}
l.Size += 1
return node
}
// 查找列表中的中间元素,若中间元素有两个,则取后一个
func (l *List) GetMiddle() *Node {
num := l.Size / 2
node := l.HeadNode
for i := 1; i <= num; i ++ {
node = node.Next
}
return node
}
func main() {
list := new(List)
list.Lock = &sync.Mutex{}
v1 := Value(1)
n1 := list.Add(v1)
v2 := Value(2)
n2 := list.Append(v2)
v3 := Value(3)
n3 := list.Append(v3)
v4 := Value(4)
n4 := list.Append(v4)
fmt.Println(*n1)
fmt.Println(*n2)
fmt.Println(*n3)
fmt.Println(*n4)
fmt.Println(list.GetMiddle())
}
// {1 0xc000098020}
// {2 0xc000098040}
// {3 0xc000098060}
// {4 <nil>}
// &{3 0xc000098060}
有序链表的合并
对于两个有序列表合并的问题,思路是分别遍历两个链表,并对其每个值取出进行对比,以升序为例,将更小值的节点优先插入新的链表,从而得到新的有序链表。
// 两个有序链表进行合并,保证合并后依然从小到大排序,并且返回合并后的头结点
func MergeSorted(node1 *Node, node2 *Node) *Node {
list := new(List)
list.Lock = &sync.Mutex{}
n1 := node1
n2 := node2
for n1 != nil && n2 != nil {
if n1.Data < n2.Data {
list.Append(n1)
n1 = n1.Next
} else {
list.Append(n2)
n2 = n2.Next
}
}
if n1 == nil {
list.Append(n2)
} else {
list.Append(n1)
}
return list.HeadNode
}