一、链表基础知识
1.1 链表结构
class ListNode:
def __init__(self, val=0, next=None):
self.val = val # 节点值
self.next = next # 指向下一个节点的指针
1.2 链表 vs 数组
| 特性 | 链表 | 数组 |
|---|---|---|
| 访问 | O(n) 顺序访问 | O(1) 随机访问 |
| 插入/删除 | O(1) 修改指针 | O(n) 移动元素 |
| 内存 | 分散,有指针开销 | 连续 |
| 索引 | ❌ 不支持 | ✅ 支持 |
二、核心技巧总结
技巧1: 虚拟头节点(Dummy Node)⭐⭐⭐
何时使用:
- 可能修改头节点的问题
- 需要返回新链表的问题
- 合并、删除节点的问题
模板:
def solution(head):
dummy = ListNode(0) # 创建虚拟头
dummy.next = head
curr = dummy
# ... 处理逻辑
return dummy.next # 返回真正的头节点
示例:删除链表中的节点
# 不用虚拟头节点(复杂)
def deleteNode(head, val):
# 特殊处理头节点
if head.val == val:
return head.next
curr = head
while curr.next:
if curr.next.val == val:
curr.next = curr.next.next
break
curr = curr.next
return head
# 用虚拟头节点(简洁)
def deleteNode(head, val):
dummy = ListNode(0)
dummy.next = head
curr = dummy
while curr.next:
if curr.next.val == val:
curr.next = curr.next.next
break
curr = curr.next
return dummy.next # 统一处理
技巧2: 快慢指针(Two Pointers)⭐⭐⭐
常见应用:
应用1: 找中点
def findMiddle(head):
"""找链表中点"""
slow = fast = head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
return slow # slow 指向中点
应用2: 判断环
def hasCycle(head):
"""判断是否有环"""
slow = fast = head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
if slow == fast:
return True
return False
应用3: 找倒数第k个节点
def findKthFromEnd(head, k):
"""找倒数第k个节点"""
fast = slow = head
# fast 先走 k 步
for _ in range(k):
if not fast:
return None
fast = fast.next
# 一起走,fast 到末尾时,slow 在倒数第k个
while fast:
slow = slow.next
fast = fast.next
return slow
应用4: 删除倒数第k个节点
def removeKthFromEnd(head, k):
"""删除倒数第k个节点"""
dummy = ListNode(0)
dummy.next = head
fast = slow = dummy
# fast 先走 k+1 步
for _ in range(k + 1):
fast = fast.next
# 一起走
while fast:
slow = slow.next
fast = fast.next
# slow.next 就是要删除的节点
slow.next = slow.next.next
return dummy.next
快慢指针速查表:
| 问题 | slow 速度 | fast 速度 | 初始位置 | 结果 |
|---|---|---|---|---|
| 找中点 | 1步 | 2步 | 都在head | slow到中点 |
| 判断环 | 1步 | 2步 | 都在head | 相遇=有环 |
| 找倒数第k个 | 1步 | 1步 | fast先走k步 | slow是目标 |
技巧3: 反转链表模板 ⭐⭐⭐
迭代版本:
def reverseList(head):
"""三指针反转"""
prev = None
curr = head
while curr:
next_node = curr.next # 1. 保存下一个
curr.next = prev # 2. 反转指针
prev = curr # 3. 移动prev
curr = next_node # 4. 移动curr
return prev
递归版本:
def reverseList(head):
"""递归反转"""
if not head or not head.next:
return head
new_head = reverseList(head.next)
head.next.next = head
head.next = None
return new_head
反转部分链表:
def reverseBetween(head, left, right):
"""反转 [left, right] 区间"""
dummy = ListNode(0)
dummy.next = head
prev = dummy
# 1. 找到 left-1 位置
for _ in range(left - 1):
prev = prev.next
# 2. 反转 [left, right]
curr = prev.next
for _ in range(right - left):
next_node = curr.next
curr.next = next_node.next
next_node.next = prev.next
prev.next = next_node
return dummy.next
技巧4: 递归思维 ⭐⭐
递归三要素:
def recursion(head):
# 1. 终止条件(Base Case)
if not head or not head.next:
return head
# 2. 递归调用(假设子问题已解决)
result = recursion(head.next)
# 3. 当前层处理
# 处理当前节点和子问题结果的关系
return result
递归模板应用:
# 删除链表中的节点
def deleteNode(head, val):
if not head:
return None
if head.val == val:
return head.next
head.next = deleteNode(head.next, val)
return head
# 合并两个有序链表
def mergeTwoLists(l1, l2):
if not l1:
return l2
if not l2:
return l1
if l1.val <= l2.val:
l1.next = mergeTwoLists(l1.next, l2)
return l1
else:
l2.next = mergeTwoLists(l1, l2.next)
return l2
技巧5: 哈希表辅助 ⭐⭐
适用场景:
- 检测环/重复
- 查找节点
- 记录访问状态
# 判断有环
def hasCycle(head):
visited = set()
curr = head
while curr:
if curr in visited:
return True
visited.add(curr)
curr = curr.next
return False
# 找环的入口
def detectCycle(head):
visited = set()
curr = head
while curr:
if curr in visited:
return curr
visited.add(curr)
curr = curr.next
return None
技巧6: 双链表拼接 ⭐
处理相交链表:
def getIntersection(headA, headB):
"""双指针消除长度差"""
pA, pB = headA, headB
while pA != pB:
pA = pA.next if pA else headB
pB = pB.next if pB else headA
return pA
三、常见问题类型及模板
类型1: 链表遍历
# 模板
def traverse(head):
curr = head
while curr:
# 处理 curr
print(curr.val)
curr = curr.next
类型2: 链表反转
# 完全反转
def reverse(head):
prev = None
curr = head
while curr:
next_node = curr.next
curr.next = prev
prev = curr
curr = next_node
return prev
# 部分反转
def reverseBetween(head, left, right):
# 使用虚拟头节点 + 头插法
pass
类型3: 链表合并
# 合并两个有序链表
def merge(l1, l2):
dummy = ListNode(0)
curr = dummy
while l1 and l2:
if l1.val <= l2.val:
curr.next = l1
l1 = l1.next
else:
curr.next = l2
l2 = l2.next
curr = curr.next
curr.next = l1 if l1 else l2
return dummy.next
类型4: 链表删除
# 删除值为val的所有节点
def deleteValue(head, val):
dummy = ListNode(0)
dummy.next = head
curr = dummy
while curr.next:
if curr.next.val == val:
curr.next = curr.next.next
else:
curr = curr.next
return dummy.next
# 删除重复节点(保留一个)
def deleteDuplicates(head):
curr = head
while curr and curr.next:
if curr.val == curr.next.val:
curr.next = curr.next.next
else:
curr = curr.next
return head
类型5: 链表排序
# 归并排序
def sortList(head):
if not head or not head.next:
return head
# 找中点
slow, fast = head, head.next
while fast and fast.next:
slow = slow.next
fast = fast.next.next
# 分割
mid = slow.next
slow.next = None
# 递归排序
left = sortList(head)
right = sortList(mid)
# 合并
return merge(left, right)
四、调试技巧
4.1 打印链表
def print_list(head, name="链表"):
values = []
curr = head
count = 0
while curr and count < 20: # 防止死循环
values.append(str(curr.val))
curr = curr.next
count += 1
print(f"{name}: {' → '.join(values)}")
4.2 可视化节点
def visualize(head):
curr = head
nodes = []
visited = set()
while curr:
if curr in visited:
nodes.append(f"({curr.val}*)←环")
break
visited.add(curr)
nodes.append(str(curr.val))
curr = curr.next
print(" → ".join(nodes))
4.3 检查环
def check_cycle(head):
"""调试时检查是否意外形成环"""
slow = fast = head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
if slow == fast:
print("⚠️ 警告:检测到环!")
return True
return False
五、常见错误及避免方法
错误1: 空指针访问
# ❌ 错误
curr = curr.next
print(curr.val) # curr 可能是 None
# ✅ 正确
if curr:
curr = curr.next
if curr:
print(curr.val)
# 或者
while curr and curr.next:
curr = curr.next
错误2: 丢失节点
# ❌ 错误
curr.next = curr.next.next # 没保存 curr.next
# curr.next 节点丢失了!
# ✅ 正确
next_node = curr.next # 先保存
curr.next = next_node.next
错误3: 死循环
# ❌ 错误:形成环
node1.next = node2
node2.next = node1 # 死循环!
# ✅ 正确:断开环
node1.next = node2
node2.next = None
错误4: 修改了不该修改的
# ❌ 错误:修改了输入链表
def process(head):
head = head.next # 只是局部变量改变
return head
# ✅ 正确:如果要修改,明确说明
def process(head):
new_head = head.next
head.next = None # 明确断开
return new_head
六、题型识别速查表
| 关键词 | 可能的解法 | 典型题目 |
|---|---|---|
| 找中点 | 快慢指针 | 链表中点、回文链表 |
| 环 | 快慢指针/哈希表 | 环形链表、环的入口 |
| 倒数第k个 | 快慢指针 | 删除倒数第k个 |
| 反转 | 迭代/递归 | 反转链表、K组反转 |
| 合并 | 双指针 | 合并有序链表 |
| 删除 | 虚拟头节点 | 删除节点、去重 |
| 相加 | 模拟 | 两数相加 |
| 相交 | 双指针拼接 | 相交链表 |
| 排序 | 归并排序 | 链表排序 |
| 随机 | 蓄水池抽样 | 随机节点 |
七、做题流程
1. 理解题意
├─ 输入是什么?
├─ 输出是什么?
├─ 有哪些限制?
└─ 边界情况?
2. 选择技巧
├─ 需要修改头节点?→ 虚拟头节点
├─ 找中点/环?→ 快慢指针
├─ 反转?→ 三指针迭代/递归
└─ 合并/删除?→ 虚拟头节点 + 双指针
3. 画图模拟
├─ 画出初始状态
├─ 画出每一步变化
└─ 确认最终状态
4. 写代码
├─ 先写主逻辑
├─ 再处理边界
└─ 添加注释
5. 测试
├─ 空链表
├─ 单节点
├─ 两个节点
└─ 正常情况
八、刷题顺序建议
入门级(必做)
- 反转链表
- 合并两个有序链表
- 删除链表节点
- 链表中点
- 环形链表
进阶级
- 环形链表II(找入口)
- 相交链表
- 回文链表
- 删除倒数第k个节点
- 两数相加
高级
- 反转链表II(部分反转)
- K个一组反转
- 排序链表
- 合并K个有序链表
- 复制带随机指针的链表
九、记忆口诀
链表题目不要慌,几个技巧记心上:
虚拟头节点,简化边界情况;
快慢双指针,中点和环都能找;
反转三步走,保存反转再移动;
递归要清晰,终止递归加处理;
画图很重要,指针变化要看清;
边界要考虑,空链单节要测试。
十、模板代码库
# 1. 虚拟头节点模板
def template_dummy(head):
dummy = ListNode(0)
dummy.next = head
curr = dummy
# ... 处理
return dummy.next
# 2. 快慢指针模板
def template_two_pointers(head):
slow = fast = head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
return slow
# 3. 反转链表模板
def template_reverse(head):
prev = None
curr = head
while curr:
next_node = curr.next
curr.next = prev
prev = curr
curr = next_node
return prev
# 4. 递归模板
def template_recursion(head):
if not head or not head.next:
return head
result = template_recursion(head.next)
# 处理当前节点
return result
# 5. 合并模板
def template_merge(l1, l2):
dummy = ListNode(0)
curr = dummy
while l1 and l2:
if l1.val <= l2.val:
curr.next = l1
l1 = l1.next
else:
curr.next = l2
l2 = l2.next
curr = curr.next
curr.next = l1 if l1 else l2
return dummy.next
掌握这些技巧和模板,链表问题就能游刃有余!关键是多画图、多模拟、多练习。
953

被折叠的 条评论
为什么被折叠?



