链表
为什么链表很重要
链表能够在任意位置高效地插入和删除元素——这些操作在数组中需要 O(n) 的元素移动:
- 动态内存分配:节点分散在内存中,不需要连续的内存分配
- 高效的插入/删除:在已知位置为 O(1)(只需更新指针)
- 实现基础:栈、队列、哈希映射链表和图邻接表的实现基础
- 面试模式:快慢指针、链表反转和循环检测在 30%+ 的链表问题中出现
实际影响:LinkedHashMap 使用双向链表来维护迭代顺序——使 entrySet() 遍历为 O(n),而不是纯 HashMap 可能的 O(n²)。
核心概念
单向链表结构
每个节点包含数据和指向下一个节点的指针:
class ListNode {
int val;
ListNode next;
ListNode(int val) { this.val = val; }
}
关键特征:
- 头指针:列表的入口点(丢失它意味着丢失整个列表)
- 最后一个节点:指向
null(列表结束标记) - 访问:O(n) 到达第 i 个元素(必须从头开始遍历)
双向链表
节点同时具有 next 和 prev 指针:
class DoublyListNode {
int val;
DoublyListNode prev;
DoublyListNode next;
DoublyListNode(int val) { this.val = val; }
}
优势:
- 双向遍历:可以向前或向后迭代
- O(1) 删除:当已知节点引用时(无需找到前驱节点)
- 支持的操作:撤销/重做、浏览器历史前进/后退
劣势:
- 2倍内存开销:每个节点有两个指针而不是一个
- 复杂性:需要维护更多的指针操作