1. 链表基础与LeetCode解题方法论
链表作为数据结构中的经典类型,在LeetCode算法题中占据重要地位。不同于数组的连续存储特性,链表通过节点和指针实现动态内存分配,这种结构特点决定了其特有的解题思路。在实际面试中,链表相关题目出现频率高达35%(根据2023年算法面试统计),掌握核心解题模式至关重要。
链表节点通常定义为:
class ListNode: def __init__(self, val=0, next=None): self.val = val self.next = next1.1 链表操作核心四要素
- 指针操作:理解cur.next = prev这样的指针重定向本质
- 边界处理:头节点、尾节点、空链表等特殊情况
- 循环条件:while cur与while cur.next的区别场景
- 虚拟头节点:dummy节点的妙用可减少条件判断
实战经验:链表题目80%的错误来源于指针操作失误,建议在纸上画出节点关系图再编码
2. LeetCode 2题:两数相加深度解析
2.1 问题重述
给定两个非空链表表示的非负整数,数字按逆序存储(如2→4→3表示342),返回两数之和的链表形式。这是链表操作与数学运算结合的典型题目。
2.2 关键解题步骤
- 初始化dummy节点和进位标志carry
- 同步遍历两个链表,处理三种情况:
- 两链表都有节点
- 仅一个链表有节点
- 两链表都无节点但存在进位
- 计算当前位和并更新carry:
total = l1.val + l2.val + carry carry = total // 10 val = total % 102.3 易错点排查表
| 错误类型 | 现象 | 解决方法 |
|---|---|---|
| 进位丢失 | 最后一位未处理carry | 循环后增加carry检查 |
| 指针越界 | 访问None的val属性 | 先判断节点非空再取值 |
| 结果逆序 | 输出变成正向数字 | 注意题目本身就是逆序存储 |
3. LeetCode 19题:删除链表的倒数第N个节点
3.1 快慢指针标准解法
- 初始化dummy节点处理头节点删除情况
- 快指针先走n步
- 快慢指针同步移动直到快指针到达末尾
- 慢指针next指向next.next完成删除
def removeNthFromEnd(head, n): dummy = ListNode(0, head) fast = slow = dummy for _ in range(n): fast = fast.next while fast.next: fast = fast.next slow = slow.next slow.next = slow.next.next return dummy.next3.2 复杂度优化对比
| 方法 | 时间复杂度 | 空间复杂度 | 适用场景 |
|---|---|---|---|
| 两次遍历 | O(L) | O(1) | 链表长度已知 |
| 快慢指针 | O(L) | O(1) | 通用解法 |
| 栈缓存 | O(L) | O(L) | 需要记录路径时 |
4. LeetCode 21题:合并两个有序链表
4.1 迭代法标准实现
def mergeTwoLists(l1, l2): dummy = cur = ListNode() while l1 and l2: if l1.val < l2.val: cur.next = l1 l1 = l1.next else: cur.next = l2 l2 = l2.next cur = cur.next cur.next = l1 or l2 return dummy.next4.2 递归解法精要
def mergeTwoLists(l1, l2): if not l1 or not l2: return l1 or l2 if l1.val < l2.val: l1.next = mergeTwoLists(l1.next, l2) return l1 else: l2.next = mergeTwoLists(l1, l2.next) return l2递归虽然简洁,但存在栈溢出风险,链表长度超过1000时建议使用迭代法
5. LeetCode 23题:合并K个升序链表
5.1 分治合并策略
- 将k个链表两两配对
- 递归合并每组链表
- 重复直到只剩一个链表
def mergeKLists(lists): if not lists: return None if len(lists) == 1: return lists[0] mid = len(lists) // 2 left = mergeKLists(lists[:mid]) right = mergeKLists(lists[mid:]) return mergeTwoLists(left, right)5.2 优先队列解法
from heapq import heappush, heappop def mergeKLists(lists): dummy = cur = ListNode() heap = [] for i, node in enumerate(lists): if node: heappush(heap, (node.val, i, node)) while heap: val, i, node = heappop(heap) cur.next = node cur = cur.next if node.next: heappush(heap, (node.next.val, i, node.next)) return dummy.next6. LeetCode 24题:两两交换链表节点
6.1 迭代法实现
def swapPairs(head): dummy = prev = ListNode(0, head) while prev.next and prev.next.next: first = prev.next second = first.next prev.next, second.next, first.next = second, first, second.next prev = first return dummy.next6.2 递归解法
def swapPairs(head): if not head or not head.next: return head new_head = head.next head.next = swapPairs(new_head.next) new_head.next = head return new_head7. 链表专题进阶技巧
7.1 常用辅助工具
- 链表可视化:
def print_list(head): while head: print(head.val, end=" -> ") head = head.next print("None")- 测试用例生成:
def create_list(nums): dummy = cur = ListNode() for num in nums: cur.next = ListNode(num) cur = cur.next return dummy.next7.2 高频考点延伸
- 环形链表检测(快慢指针)
- 链表排序(归并排序)
- 复杂链表复制(哈希表/节点拆分)
- LRU缓存实现(哈希表+双向链表)
在实际刷题过程中,建议先手动画出链表变换示意图,再转化为代码实现。对于容易混淆的指针操作,可以使用不同颜色的箭头标注next指针的变化方向。我个人的经验是,当遇到链表问题时,先问自己三个问题:是否需要dummy节点?循环终止条件是什么?指针操作的顺序是否会影响结果?