链表的核心实现解析

222 阅读5分钟

链表

image.png 链表属于线性表,链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。相较于顺序表,相较于顺序表

优点

  • 不需要分配连续的内存空间
  • 不需要动态扩容
  • 在列表中间添加、删除节点不需要整体移动元素

缺点

  • 需要额外的内存空间存放指针
  • 查询需要依次遍历每个节点进行查找

基本种类

  • 单向链表
  • 双向链表
  • 循环链表

单链表

单链表每个节点的结构具有value节点值和next指针两个属性,我们可以从头指针开始通过next指针查找到链表上的每个元素。 image.png

创建单链表的类型结构体


// 单链表结构体
type List struct {

   // 单链表第一个节点指针
   first *element
   
   // 单链表最后一个节点指针
   last  *element
   
   // 单链表的长度
   size  int
}

// 每个节点的结构体
type element struct {

   // 节点值
   value interface{}
   
   // 下一个节点的指针
   next  *element
}

创建一个基本的单链表

// New 创建一个单链表
func New(values ...interface{}) *List {
   list := &List{}
   if len(values) > 0 {
      list.Add(values...)
   }
   return list
}


链表在末尾添加节点(尾插法)

未命名文件.png 代码实现

// Add 在链表末尾添加一个元素
func (list *List) Add(values ...interface{}) {

   // 遍历需要插入到链表的值列表
   for _, value := range values {

      // 创建一个新的节点,值为要插入的值
      newElement := &element{value: value}

      // 如果当前链表的大小为0,则链表的头尾指针都指向这个新的节点
      if list.size == 0 {
         list.first = newElement
         list.last = newElement
      } else {

         // 链表有其他节点的情况下,向链表的末尾添加新的节点
         // 1. 将链表的尾指针的next指向新的节点
         list.last.next = newElement

         // 2. 将这个新的节点作为尾指针
         list.last = newElement
      }
      list.size++
   }
}

在链表头部插入新节点(头插法)

未命名文件 (1).png

func (list *List) Prepend(values ...interface{}) {
   // 反向遍历要插入的节点列表,保证插入顺序,比如:["c","d"] -> Prepend(["a","b"]) -> ["a","b","c",d"]
   for v := len(values) - 1; v >= 0; v-- {
      // 1. 创建一个新的节点,新节点的next指向链表的头结点
      newElement := &element{value: values[v], next: list.first}
      
      // 2. 再将新节点作为链表的头结点
      list.first = newElement
      
      // 3. 如果是空链表,链表的尾结点也指向新节点
      if list.size == 0 {
         list.last = newElement
      }
      list.size++
   }
}

获取链表第index个节点

// 索引越界判断
func (list *List) withinRange(index int) bool {
   return index >= 0 && index < list.size
}

// Get 获取链表第index个元素值
func (list *List) Get(index int) (interface{}, bool) {
   // 如果索引越界
   if !list.withinRange(index) {
      return nil, false
   }
   element := list.first
   // 遍历 当e等于index,对应的element就是目标节点
   // 0 -> 头结点
   // 1 -> 第二个节点
   //  ...
   // index -> 第index+1个节点(目标节点)
   
   // 示例:
   // 在链表0->1->2->3中 如果index = 2
   // 0->头结点  1->第二个节点 2->第三个节点
   // 所以目标节点为2
   for e := 0; e != index; e, element = e+1, element.next {
   }
   return element.value, true
}

删除第index个节点

未命名文件 (2).png

func (list *List) Remove(index int) {

   if !list.withinRange(index) {
      return
   }

   if list.size == 1 {
      list.Clear()
      return
   }

   // 要删除的节点的前一个节点
   var beforeElement *element

   // 要删除的节点
   element := list.first
   for e := 0; e != index; e, element = e+1, element.next {
      beforeElement = element
   }

   // 如果要删除的节点是第一个节点
   if element == list.first {
      list.first = element.next
   }

   // 如果要删除的节点是最后一个节点
   if element == list.last {
      list.last = beforeElement
   }

   // 如果上一个节点不为空,上一个节点的next指针越过element节点
   if beforeElement != nil {
      beforeElement.next = element.next
   }

   element = nil

   list.size--
}

在目标位置插入节点列表

未命名文件 (3).png

// Insert 在目标位置插入节点
func (list *List) Insert(index int, values ...interface{}) {

   if !list.withinRange(index) {
      if index == list.size {
         list.Add(values...)
      }
      return
   }

   // 链表长度加上插入节点的个数
   list.size += len(values)

   // foundElement最终指向要插入节点位置的节点
   // beforeElement最终指向要插入节点位置前一个节点
   // 3->4->5->6 index=2
   var beforeElement *element
   foundElement := list.first
   for e := 0; e != index; e, foundElement = e+1, foundElement.next {
      beforeElement = foundElement
   }

   // 如果foundElement是头结点位置,可以直接调用list.Prepend(values...),或者下面的另一种实现
   if foundElement == list.first {
      // 保留旧节点列表
      oldNextElement := list.first
      // 遍历要插入的节点列表
      for i, value := range values {
         // 创建一个新节点值为value
         newElement := &element{value: value}
         if i == 0 {
            // 如果newElement是插入列表中的第一个节点,将newElement作为新的头结点
            list.first = newElement
         } else {
            // 不是第一个节点,将新节点插入到beforeElement之后
            beforeElement.next = newElement
         }
         // 更新beforeElement,将这次的newElement作为下一次的beforeElement
         beforeElement = newElement
      }
      // 连接上后面的节点
      beforeElement.next = oldNextElement
   } else {
      // 保留beforeElement后面原有的链表的指针
      oldNextElement := beforeElement.next
      for _, value := range values {
         // 创建新的节点
         newElement := &element{value: value}
         // beforeElement的next指向新节点
         beforeElement.next = newElement
         // 将新节点作为下一次的beforeElement
         beforeElement = newElement
      }
      // 拼接原有的链表
      beforeElement.next = oldNextElement
   }
}

其他函数


// Size 返回链表大小
func (list *List) Size() int {
   return list.size
}

// Clear 清空链表
func (list *List) Clear() {
   list.size = 0
   list.first = nil
   list.last = nil
}

// Set 设置目标索引位置的值
func (list *List) Set(index int, value interface{}) {

   if !list.withinRange(index) {
      // 位置等于链表长度,在链表末尾添加节点元素
      if index == list.size {
         list.Add(value)
      }
      return
   }

   foundElement := list.first
   for e := 0; e != index; {
      e, foundElement = e+1, foundElement.next
   }
   foundElement.value = value
}

// String 字符串序列化
func (list *List) String() string {
   str := "SinglyLinkedList\n"
   values := []string{}
   for element := list.first; element != nil; element = element.next {
      values = append(values, fmt.Sprintf("%v", element.value))
   }
   str += strings.Join(values, " -> ")
   return str
}

// IndexOf 在链表中查找目标值索引
func (list *List) IndexOf(value interface{}) int {
   if list.size == 0 {
      return -1
   }
   for index, element := range list.Values() {
      if element == value {
         return index
      }
   }
   return -1
}

func (list *List) Empty() bool {
   return list.size == 0
}

双向链表

双向链表除了指向下一个节点的指针next,还有一个指向上一个节点的指针prev,这样就可以在链表中前后查找、头尾并行查找。

image.png

定义结构体

type List struct {
   first *element
   last  *element
   size  int
}

type element struct {
   value interface{}
   prev  *element
   next  *element
}

创建一个基本的双链表

func New(values ...interface{}) *List {
   list := &List{}
   if len(values) > 0 {
      list.Add(values...)
   }
   return list
}

双向链表末尾添加节点

func (list *List) Add(values ...interface{}) {
   for _, value := range values {
       // 创建的新节点的prev执行链表的尾结点,后面再改变list.last的指针
      newElement := &element{value: value, prev: list.last}
      if list.size == 0 {
         list.first = newElement
         list.last = newElement
      } else {
         list.last.next = newElement
         list.last = newElement
      }
      list.size++
   }
}

双向链表头部插入新的节点列表

func (list *List) Prepend(values ...interface{}) {
   for v := len(values) - 1; v >= 0; v-- {
      newElement := &element{value: values[v], next: list.first}
      if list.size == 0 {
         list.first = newElement
         list.last = newElement
      } else {
         list.first.prev = newElement
         list.first = newElement
      }
      list.size++
   }
}

双向链表删除第index个节点

func (list *List) Remove(index int) {

   if !list.withinRange(index) {
      return
   }

   if list.size == 1 {
      list.Clear()
      return
   }

   var element *element
   // determine traversal direction, last to first or first to last
   if list.size-index < index {
      element = list.last
      for e := list.size - 1; e != index; e, element = e-1, element.prev {
      }
   } else {
      element = list.first
      for e := 0; e != index; e, element = e+1, element.next {
      }
   }

   if element == list.first {
      list.first = element.next
   }
   if element == list.last {
      list.last = element.prev
   }
   if element.prev != nil {
      element.prev.next = element.next
   }
   if element.next != nil {
      element.next.prev = element.prev
   }

   element = nil

   list.size--
}

双向链表在目标位置插入节点列表

func (list *List) Insert(index int, values ...interface{}) {

   if !list.withinRange(index) {
      if index == list.size {
         list.Add(values...)
      }
      return
   }

   list.size += len(values)

   var beforeElement *element
   var foundElement *element
   if list.size-index < index {
      foundElement = list.last
      for e := list.size - 1; e != index; e, foundElement = e-1, foundElement.prev {
         beforeElement = foundElement.prev
      }
   } else {
      foundElement = list.first
      for e := 0; e != index; e, foundElement = e+1, foundElement.next {
         beforeElement = foundElement
      }
   }

   if foundElement == list.first {
      oldNextElement := list.first
      for i, value := range values {
         newElement := &element{value: value}
         if i == 0 {
            list.first = newElement
         } else {
            newElement.prev = beforeElement
            beforeElement.next = newElement
         }
         beforeElement = newElement
      }
      oldNextElement.prev = beforeElement
      beforeElement.next = oldNextElement
   } else {
      oldNextElement := beforeElement.next
      for _, value := range values {
         newElement := &element{value: value}
         newElement.prev = beforeElement
         beforeElement.next = newElement
         beforeElement = newElement
      }
      oldNextElement.prev = beforeElement
      beforeElement.next = oldNextElement
   }
}