跳到主要内容

Go 链表详解

初始化

在 Go 语言中,container/list 包提供了对双向链表的支持。我可以通过以下几种方式来初始化一个链表:

package main

import (
"container/list"
)

func main() {
// 方法一:直接声明一个链表变量
var linkedList list.List

// 方法二:使用 new 函数并调用 Init 方法
linkedList2 := new(list.List).Init()

// 方法三:使用 list 包的 New 函数
linkedList3 := list.New()
}

链表的结构

链表中的每个元素都是一个 Element 类型,其结构如下所示:

type Element struct {
// Next 和 Prev 指向链表中下一个和上一个元素
Next, Prev *Element

// List 指向链表本身,用于维护链表结构
List *List

// Value 存储节点的实际值,类型为 interface{}
Value interface{}
}

操作链表

我将演示如何对链表进行基本的操作。

package main

import (
"container/list"
"fmt"
)

func main() {
var linkedList list.List

// 在链表末尾添加元素 "go"
linkedList.PushBack("go")

// 在链表开头添加元素 "sumingcheng"
linkedList.PushFront("sumingcheng")

// 再次在链表末尾添加元素 "python"
linkedList.PushBack("python")

// 打印链表长度
fmt.Println("链表长度", linkedList.Len())

// 遍历并打印链表元素
for element := linkedList.Front(); element != nil; element = element.Next() {
fmt.Println(element.Value)
}

// 从后向前遍历并打印链表元素
for element := linkedList.Back(); element != nil; element = element.Prev() {
fmt.Println(element.Value)
}

// 获取并打印链表的第一个和最后一个元素
fmt.Println("第一个元素", linkedList.Front().Value)
fmt.Println("最后一个元素", linkedList.Back().Value)
}

链表的方法

链表提供了一些常用的方法:

方法描述
PushBack(v interface) *Element在链表末尾插入一个新元素,返回新元素的引用
PushFront(v interface) *Element在链表开头插入一个新元素,返回新元素的引用
Front() *Element返回链表的第一个元素,如果链表为空则返回 nil
Back() *Element返回链表的最后一个元素,如果链表为空则返回 nil
Len() int返回链表中元素的数量

链表的移动

我可以通过以下方式移动链表中的元素,例如将最后一个元素移动到链表的前面。

package main

import (
"container/list"
"fmt"
)

func moveLastToFront(ll *list.List) {
if ll.Len() > 1 {
lastElement := ll.Back()
ll.MoveToFront(lastElement)
}
}

func printList(ll *list.List) {
for element := ll.Front(); element != nil; element = element.Next() {
fmt.Println(element.Value)
}
}

func main() {
linkedList := list.New()
linkedList.PushBack("sumingcheng")
linkedList.PushBack("go")
linkedList.PushBack("python")

moveLastToFront(linkedList)
printList(linkedList)
}

链表的追加

我可以向链表中添加元素:

package main

import (
"container/list"
)

func main() {
linkedList := list.New()
linkedList.PushBack(1)
linkedList.PushBack(2)
linkedList.PushBack(3)
}

两个链表的连接

可以将一个链表追加到另一个链表的末尾:

package main

import (
"container/list"
)

func linkListsDirectly(l1, l2 *list.List) {
if l2.Len() > 0 {
l1.PushBackList(l2)
}
}

func main() {
list1 := list.New()
list1.PushBack(1)
list1.PushBack(2)
list1.PushBack(3)

list2 := list.New()
list2.PushBack(4)
list2.PushBack(5)
list2.PushBack(6)

linkListsDirectly(list1, list2)
}

链表的删除

当我需要删除链表中的某个元素时,需要注意以下几点。

当删除链表的一个元素时,该元素被移除,无法再通过该元素访问链表的其他部分。为了安全地继续遍历,需要在删除元素之前获取下一个元素的引用。

func removeElements(ll *list.List, value interface{}) {
for element := ll.Front(); element != nil; {
next := element.Next() // 保存下一个元素的引用
if element.Value == value {
ll.Remove(element) // 删除当前元素
}
element = next // 移动到下一个元素
}
}

链表的修改

我可以遍历链表,修改特定元素的值:

func updateElements(ll *list.List, oldValue, newValue interface{}) {
for element := ll.Front(); element != nil; element = element.Next() {
if element.Value == oldValue {
element.Value = newValue
}
}
}

链表的反转

以下函数可以实现链表的反转。

func reverseList(ll *list.List) *list.List {
reversed := list.New()
for element := ll.Front(); element != nil; {
next := element.Next() // 保存下一个元素的引用
ll.Remove(element) // 从原链表中移除当前元素
reversed.PushFront(element.Value) // 将元素添加到新链表的前端
element = next
}
return reversed
}

注意 📝 当操作链表时,务必注意元素的引用和链表的结构,以避免意外的错误。