一句话总结
链表高频题核心技巧:1)哑节点(dummy)简化头节点处理;2)快慢指针解决环检测、找中点、倒数第 K 个;3)多指针(prev/curr/next)解决反转、交换;4)递归优雅解决反转、合并。时间复杂度通常 O(n),空间 O(1)(迭代)或 O(n)(递归栈)。
初级理解
反转链表(LeetCode 206)
# 迭代法(三指针)
def reverse_list(head):
prev = None
curr = head
while curr:
next_temp = curr.next # 保存下一个
curr.next = prev # 反转
prev = curr # 移动 prev
curr = next_temp # 移动 curr
return prev
# 递归法
def reverse_list_recursive(head):
if not head or not head.next:
return head
new_head = reverse_list_recursive(head.next)
head.next.next = head # 反转
head.next = None
return new_head
合并两个有序链表(LeetCode 21)
def merge_two_lists(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 or l2 # 剩余部分
return dummy.next
一句话总结:反转用三指针(prev/curr/next),合并用哑节点简化边界。
中级深入
环形链表检测(LeetCode 141 + 142)
# 判断是否有环(快慢指针)
def has_cycle(head):
slow = fast = head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
if slow == fast:
return True
return False
# 找到入环点
def detect_cycle(head):
slow = fast = head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
if slow == fast: # 相遇
# 从头和相遇点同时走,再次相遇就是入环点
ptr = head
while ptr != slow:
ptr = ptr.next
slow = slow.next
return ptr
return None
# 原理:
# 设头到入环点距离 a,入环点到相遇点距离 b,相遇点到入环点距离 c
# 慢指针走 a+b,快指针走 a+b+n(b+c)
# 快指针速度是慢指针 2 倍:2(a+b) = a+b+n(b+c)
# → a = (n-1)(b+c) + c
# → 从头走 a 步 = 从相遇点走 c + n-1 圈 → 在入环点相遇
删除倒数第 N 个节点(LeetCode 19)
def remove_nth_from_end(head, n):
dummy = ListNode(0, head)
fast = slow = dummy
# fast 先走 n+1 步
for _ in range(n + 1):
fast = fast.next
# fast 和 slow 同时走
while fast:
fast = fast.next
slow = slow.next
# slow 指向倒数第 n+1 个节点
slow.next = slow.next.next
return dummy.next
中级要点:快慢指针解决环检测和倒数第 K 个;哑节点简化删除头节点的边界。
高级拓展
K 个一组反转链表(LeetCode 25)
def reverse_k_group(head, k):
# 检查是否有 k 个节点
curr = head
count = 0
while curr and count < k:
curr = curr.next
count += 1
if count < k:
return head
# 反转前 k 个
prev = None
curr = head
for _ in range(k):
next_temp = curr.next
curr.next = prev
prev = curr
curr = next_temp
# head 变成尾节点,连接下一组
head.next = reverse_k_group(curr, k)
return prev # prev 是新的头节点
链表排序(LeetCode 148)— 归并排序
def sort_list(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 = sort_list(head)
right = sort_list(mid)
return merge_two_lists(left, right)
实战场景
场景:LRU 缓存(LeetCode 146)
class LRUCache:
def __init__(self, capacity):
self.cap = capacity
self.cache = {} # key → Node
# 双向链表:head ↔ ... ↔ tail
self.head = Node(0, 0) # 哨兵头
self.tail = Node(0, 0) # 哨兵尾
self.head.next = self.tail
self.tail.prev = self.head
def get(self, key):
if key not in self.cache:
return -1
node = self.cache[key]
self._move_to_head(node) # 移到头部
return node.val
def put(self, key, value):
if key in self.cache:
node = self.cache[key]
node.val = value
self._move_to_head(node)
else:
node = Node(key, value)
self.cache[key] = node
self._add_to_head(node)
if len(self.cache) > self.cap:
removed = self._remove_tail()
del self.cache[removed.key]
面试模拟
面试官:怎么判断链表有环?找到入环点?
你:快慢指针,快指针每次走两步,慢指针每次走一步。如果相遇则有环。找入环点:相遇后,一个指针从头开始,一个从相遇点开始,同时每次走一步,再次相遇就是入环点。原理是数学推导:a = (n-1)(b+c) + c。
面试官:反转链表的递归思路?
你:递归到最后一个节点作为新头返回。回溯时,head.next.next = head 让下一个节点指向当前节点,head.next = None 断开原来的指向。关键是理解递归的"递"和"归":递到最深处,归时逐层反转。