30 秒识别Q1 · 83 Remove Duplicates from Sorted List:删除已排序链表中所有重复的节点,每个值只保留一个。你的第一反应是哪个模式?
看答案
链表。改指针类,cur.next = cur.next.next 跳过重复即可,连哨兵都不需要(头节点一定保留)。用 set 去重也能过,但那是数组思维,浪费了「已排序」。
next 存下来;跑指针靠一快一慢制造出位置差。改指针类(反转、合并、删除、重排)的通用保险是哨兵节点 dummy:它让「头节点也可能被删 / 被换」这个特判彻底消失,代码短一半、bug 少一半。
跑指针类(找中点、判环、倒数第 n 个)本质是第 02 章的快慢指针,只是数据结构换成了链表。画图,不要心算——三个节点、两轮循环,纸上画一遍比在脑子里推十遍准。
| 题面出现… | 想到 |
|---|---|
| 「反转 / 翻转链表(或其中一段)」 | 三指针改写(模板 A) |
| 「合并两个有序链表」「删除倒数第 n 个」 | 哨兵 + 双指针(模板 B) |
| 「找中点 / 判断有环 / 环的入口」 | 快慢指针,快的每次两步 |
| 「重排 / 判回文链表」 | 找中点 → 反转后半 → 交替拼接,三个已知动作的组合 |
| 「带随机指针的深拷贝」 | 哈希表:旧节点 → 新节点(第 01 章的映射思维) |
| 「O(1) 时间的 get 和 put」 | 哈希表 + 双向链表(146 LRU) |
快慢指针与第 02 章同源,区别只是数组能随机访问、链表只能顺着走。「合并 K 个有序链表」的主语已经不是链表而是堆(第 08 章)——两个链表用双指针,K 个链表要的是「当前最小的头节点」这种 Top-K 结构。「链表排序」考的是归并思路,不在阈值范围内。
■ 【骨架】 不变,闭着眼默写 ■ 【插槽】 随题变化,写题前先问自己填什么
def reverse_list(head):
prev, cur = None, head # 【骨架】prev 是已反转部分的头
while cur: # 【骨架】
nxt = cur.next # 【骨架】先记住 next —— 全章第一守则
cur.next = prev # 【插槽】改指针:反转 / 断开 / 指向别处
prev, cur = cur, nxt # 【骨架】两个指针一起前进
return prev # 【插槽】答案:新头 / 新尾 / 一段的两端
def merge_two(a, b):
dummy = tail = ListNode() # 【骨架】哨兵头,省掉"第一个节点"的特判
while a and b: # 【骨架】
if a.val <= b.val: # 【插槽】选谁接上来的条件
tail.next, a = a, a.next # 【骨架】接上 + 被选中的一方前进
else:
tail.next, b = b, b.next # 【骨架】
tail = tail.next # 【骨架】
tail.next = a or b # 【插槽】收尾:剩下的一整段直接挂上
return dummy.next # 【骨架】哨兵的下一个才是真头
dummy。最后 return dummy.next,不是 return head。reverse(head.next) 之后head.next.next = head; head.next = None,栈深度 O(n) 是它的代价。tail.next = a or b 是链表相对数组的便宜:剩下的一整段不用逐个搬。n + 1 步。为什么是 n + 1 而不是 n:要让 slow 停在待删节点的前一个。next 和 random。O(1) 空间的原地交织解法是加分项,不是必需品。OrderedDict 一行搞定,但必须能手写双向链表版本——考的就是这个。cur.next = prev 之后原来的 cur.next 就永远找不回来了。任何改指针的循环,第一行都应该是 nxt = cur.next。dummy 就得为「删的是头」单独写一段,这段代码是链表题最常见的漏写。while fast and fast.next 缺一不可:只判 fast 会在 fast.next.next 处崩,只判 fast.next 会在链表为空时崩。prev.next 和 next.prev,只改一边会留下悬挂引用,表现为容量统计对不上。30 秒识别Q1 · 83 Remove Duplicates from Sorted List:删除已排序链表中所有重复的节点,每个值只保留一个。你的第一反应是哪个模式?
链表。改指针类,cur.next = cur.next.next 跳过重复即可,连哨兵都不需要(头节点一定保留)。用 set 去重也能过,但那是数组思维,浪费了「已排序」。
30 秒识别Q2 · 287 Find the Duplicate Number:长度 n+1 的数组,元素都在 1..n 之间,找出那个重复的数(不能改数组、只能用 O(1) 空间)。你的第一反应是哪个模式?
链表的快慢指针。把 i -> nums[i] 看成链表的 next,重复的数就是环的入口,142 原样套用。这是本章最值得认脸的一道——它长得完全不像链表题。(允许用哈希时当然是第 01 章。)
30 秒识别Q3 · 155 Min Stack:设计一个栈,push / pop / top / getMin 全部 O(1)。你的第一反应是哪个模式?
栈,不是本章——同样是「设计一个数据结构」,146 LRU 要的是「按访问顺序淘汰」所以需要双向链表,而这道要的是「随时取当前最小」,辅助栈同步记录即可。设计题不都是链表题,看它要维护的到底是什么顺序。
每章自测都会混入恰好一道非本章题,防止「读哪章选哪章」的条件反射;题面也一律取自本章未收录的题,避免凭记忆而非凭信号作答。