数组与链表的对比,及Go语言对链表的实现

1,935 阅读3分钟

单链表的定义

单链表是一种顺序存储的数据结构,链表由一系列的结点组成,每个节点包含两部分,一个是存储数据元素的数据域,另一个是存储下一个节点的指针域。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
}