什么是链表?
链表是一种数据结构,它由一系列节点组成,每个节点包含数据和指向其他节点的指针。相比于数组,链表不需要预先指定容量,可以在运行时动态添加或删除节点,灵活度更高。
链表的种类
链表主要有两种类型:单向链表、双向链表。单向链表每个节点只有一个指针,指向下一个节点;双向链表每个节点有两个指针,一个指向前一个节点,一个指向下一个节点。
另外如果链表的最后一个节点的指针指向第一个节点,形成一个循环,那么这个链表就是循环链表。
链表的基本操作
链表的基本操作包括遍历、插入、删除和查找。
- 遍历:从链表的头节点开始,依次访问链表中的每个节点。
- 插入:将节点添加到链表的指定位置。
- 删除:删除指定位置或者值的节点。
- 查找:按位置或特定值查找节点。
代码实现
这里给出单链表的代码实现样例:
package main
import "fmt"
// Node 节点
type Node struct {
// 节点数据
data int
// 指向下一个节点的指针
next *Node
}
// LinkedList 链表
type LinkedList struct {
// 头节点
head *Node
}
// Traverse 遍历链表
func (l *LinkedList) Traverse() {
if l.head == nil {
return
}
current := l.head
for current != nil {
fmt.Printf("%d ", current.data)
current = current.next
}
fmt.Println()
}
// Add 添加链表节点,这里直接添加到末尾
func (l *LinkedList) Add(data int) {
newNode := &Node{data: data}
if l.head == nil {
l.head = newNode
} else {
current := l.head
for current.next != nil {
current = current.next
}
current.next = newNode
}
}
// Remove 移除链表节点,这里根据节点值进行移除
func (l *LinkedList) Remove(data int) {
if l.head == nil {
return
}
if l.head.data == data {
l.head = l.head.next
} else {
current := l.head
for current.next != nil {
if current.next.data == data {
current.next = current.next.next
return
}
current = current.next
}
}
}
// Find 查找操作,这里根据节点的值进行查找
func (l *LinkedList) Find(data int) *Node {
current := l.head
for current != nil {
if current.data == data {
return current
}
current = current.next
}
return current
}
func main() {
l := LinkedList{}
l.Add(1)
l.Add(2)
l.Add(3)
// 2
fmt.Println(l.Find(2).data)
// 1 2 3
l.Traverse()
l.Remove(2)
// <nil>
fmt.Println(l.Find(2))
// 1 3
l.Traverse()
}
链表的优缺点
链表相对于数组具有更好的动态性能,可以在运行时动态添加、删除节点,不需要预先指定容量。但相对于数组具有更差的随机访问性能,因为查找节点的时候,都需要从头节点开始一路找过去,所以访问节点的时间复杂度是O(n)。
链表的应用
链表常用于实现其他数据结构,如栈、队列和哈希表等。比如处理哈希冲突的一个经典方法就是在每个哈希桶中维护一个链表,将被映射到相同的哈希桶的元素都放进这个链表中,取元素的时候先做哈希映射,再从链表中查找元素。
总结
- 链表是一种常用的数据结构,具有动态性能好、随机访问性能差等特点。
- 链表主要有两种类型:单向链表、双向链表。尾节点的下一个节点指向头节点,形成一个循环的链表叫循环链表。
- 链表常用于辅助实现栈、队列、哈希表等数据结构。