2. 链表(Linked List)
2. 链表(Linked List)
问题:需要频繁在中间插入/删除元素,数组搬运代价太高。
核心:每个节点存储数据和指向下一个节点的指针,内存不需要连续。
特点
- 非连续内存,通过指针串联
- 插入/删除只需修改指针,O(1)(已知位置时)
- 不支持随机访问,只能从头遍历
优点
- 插入/删除快(已知前驱节点时 O(1))
- 大小动态,不需要预分配
- 内存不需要连续块
缺点
- 随机访问 O(n),必须从头遍历
- 每个节点额外存储指针,内存开销大
- 缓存不友好(内存不连续,CPU 预读失效)
class Node {
constructor(val) {
this.val = val;
this.next = null;
}
}
class LinkedList {
head = null;
// 头部插入 O(1)
prepend(val) {
const node = new Node(val);
node.next = this.head;
this.head = node;
}
// 查找 O(n)
find(val) {
let cur = this.head;
while (cur) {
if (cur.val === val) return cur;
cur = cur.next;
}
return null;
}
// 在 target 节点后插入 O(1)
insertAfter(target, val) {
const node = new Node(val);
node.next = target.next;
target.next = node;
}
// 遍历
toArray() {
const res = [];
let cur = this.head;
while (cur) {
res.push(cur.val);
cur = cur.next;
}
return res;
}
}
const list = new LinkedList();
list.prepend(3);
list.prepend(2);
list.prepend(1);
console.log(list.toArray()); // [1, 2, 3]package linkedlist
type Node struct {
Val int
Next *Node
}
type LinkedList struct {
Head *Node
}
// 头部插入 O(1)
func (l *LinkedList) Prepend(val int) {
node := &Node{Val: val, Next: l.Head}
l.Head = node
}
// 查找 O(n)
func (l *LinkedList) Find(val int) *Node {
for cur := l.Head; cur != nil; cur = cur.Next {
if cur.Val == val {
return cur
}
}
return nil
}
// 在 target 节点后插入 O(1)
func (l *LinkedList) InsertAfter(target *Node, val int) {
node := &Node{Val: val, Next: target.Next}
target.Next = node
}链表是栈、队列的底层实现基础;JS 用
Object模拟节点,Go 标准库container/list提供双向链表。