在算法面试里,有一种数据结构,代码不长,但坑极多;
写的时候感觉没问题,一运行就null、undefined、指针飞了;
面试官一追问: “为什么要用 dummy 节点?”
你就开始怀疑人生。
没错,它就是——链表(Linked List) 。
这篇文章,我会完全基于你提供的那些“高频必考素材”,
从面试视角出发,带你系统吃透链表算法:
- dummy 哨兵节点为什么是“面试外挂”
- 删除节点到底在删谁?
- 链表反转为什么是指针操作的分水岭
- 快慢指针是怎么“凭空定位”倒数第 N 个节点的
- 如何在面试中,把思路讲清楚,而不是只会背模板
目标只有一个:
👉 让你在面试中,写得出、讲得清、还显得很专业。
一、为什么链表总是面试“重灾区”?
数组 vs 链表,本质差别只有一句话:
数组靠下标,链表靠指针。
但正是这个“靠指针”,让链表成为面试官的快乐源泉:
- 没有前驱 / 后继怎么办?
- 删除头节点是不是要特判?
- 指针一断,后面的节点去哪了?
所以面试官问链表,从来不是想看你 API 用得熟不熟,
而是想看你:
👉 有没有指针思维。
而这一切的核心钥匙,就是下面这个东西。
二、dummy 节点:链表算法里的“外挂装备”
1️⃣ dummy 节点是什么?
dummy 节点(也叫哨兵节点):
- 人为创建的假节点
- 不存储真实数据
- 通常放在链表最前面
const dummy = new ListNode(0)
dummy.next = head
它的存在只有一个目的:
抹平边界条件。
2️⃣ 为什么面试官都爱 dummy?
我们来想一个经典问题:
删除链表中值为
val的节点
如果没有 dummy,你一定写过这种代码:
if (head && head.val === val) {
return head.next
}
这就是问题:
- 头节点 没有前驱
- 必须写特判
- 逻辑被拆成两套
而用了 dummy 之后:
let cur = dummy
while (cur.next) {
if (cur.next.val === val) {
cur.next = cur.next.next
break
}
cur = cur.next
}
🎯 统一逻辑、没有特判、思路清晰。
面试官看到 dummy,潜台词只有一句:
👍 这个人写过不少链表题。
三、删除链表节点:你真正删的是谁?
面试官常问:
“删除一个节点,你删的是当前节点吗?”
答案是:
❌ 不是,你删的是前驱的 next。
链表里没有“删除自己”这种操作,只有:
前一个节点.next = 被删节点.next
经典删除模板(dummy 版本)
function remove(head, val) {
const dummy = new ListNode(0)
dummy.next = head
let cur = dummy
while (cur.next) {
if (cur.next.val === val) {
cur.next = cur.next.next
break
}
cur = cur.next
}
return dummy.next
}
💡 面试讲解时可以这样说:
“我用一个 dummy 节点,让每个节点都有前驱,
删除操作本质就是修改前驱节点的 next 指针。”
这是加分回答。
四、链表反转:指针操作的分水岭
如果说删除节点是入门题,
那链表反转就是:
🚪 面试是否放你进下一轮的门槛。
反转为什么这么重要?
因为它考察的是:
- 指针修改顺序
- 是否会“断链”
- 对当前 / 下一个节点的掌控
核心思想:头插法 + dummy
function reverseList(head) {
const dummy = new ListNode(0)
let cur = head
while (cur) {
const next = cur.next // ① 先保存下一个节点
cur.next = dummy.next // ② 指向已反转部分的头
dummy.next = cur // ③ 成为新的反转头
cur = next // ④ 继续遍历原链表
}
return dummy.next
}
面试官最爱追问的一句:
“为什么一定要先保存 next?”
标准回答:
👉 因为一旦修改
cur.next,
原链表的后半部分就可能丢失。
先保存,再断链,是所有指针题的铁律。
五、快慢指针:链表里的“时间魔法”
快慢指针的本质是:
通过速度差,制造信息差。
1️⃣ 判断链表是否有环
function hasCycle(head) {
let slow = head
let fast = head
while (fast && fast.next) {
slow = slow.next
fast = fast.next.next
if (slow === fast) return true
}
return false
}
解释给面试官听:
“如果链表有环,快指针一定会在环内追上慢指针;
如果没有环,快指针会先到 null。”
这道题考的是:
- 指针同步移动
- 循环退出条件
六、删除倒数第 N 个节点:面试高频王中王
面试官表面在问:
删除倒数第 N 个节点
实际在考:
- dummy 节点
- 快慢指针
- 指针距离控制
核心思路一句话版:
让快指针先走 N 步,制造一个“固定距离”。
const removeNthFromEnd = function(head, n) {
const dummy = new ListNode(0)
dummy.next = head
let fast = dummy
let slow = dummy
for (let i = 0; i < n; i++) {
fast = fast.next
}
while (fast.next) {
fast = fast.next
slow = slow.next
}
slow.next = slow.next.next
return dummy.next
}
为什么一定要 dummy?
因为:
- 可能删除的是头节点
- slow 必须停在 目标节点的前一个
dummy 再一次拯救了边界条件。
七、面试总结:链表题的“通关口诀”
最后,送你一套真正有用的链表面试 checklist:
✅ 只要涉及删除 / 反转 → 先想 dummy
✅ 改指针前 → 先保存 next
✅ 倒数 / 环 / 中点 → 快慢指针
✅ 永远想清楚:
👉 这一轮指针,和上一轮的关系是什么?
如果你能在面试中,把这些逻辑讲清楚,
链表题就不再是扣分项,而是:
🌟 你的加分表演时间。
如果你正在刷 LeetCode Hot100,
这套思维可以覆盖 80% 的链表题型。