只给链表中的某个节点,如何删除这个节点?
简化版
如果只给要删除的节点 node,且它不是尾节点,可以把下一个节点的值复制到当前节点,再跳过下一个节点:node.val = node.next.val; node.next = node.next.next。本质不是删除当前物理节点,而是用下一个节点覆盖当前节点的内容。
详细版
单链表删除节点通常需要前驱节点,因为要让 prev.next = node.next。只给 node 时拿不到前驱,所以无法真正让前驱跳过当前节点。面试题的限制通常会说明“该节点不是尾节点”,于是可以偷换成删除下一个节点:
A -> B -> C -> D
delete B:
copy C into B, then skip C
A -> C -> D
注意:这个方法不能用于尾节点,也不适合节点身份有外部引用、对象包含复杂资源或要求保持节点对象不变的场景。
完整版教学
一、单链表正常删除为什么需要前驱
单链表节点只知道自己的后继,不知道前驱。要删除节点 B,正常做法是让 A 指向 C。
A -> B -> C
delete B:
A.next = C
问题是,如果函数只给了 B,你无法直接找到 A。单链表不能反向走。
记忆钩子:删节点本质是改前驱的 next;没有前驱,就只能借后继做覆盖。
二、题目限制为什么要求不是尾节点
如果 node 是尾节点,它没有 next,就没有后继可以复制。
A -> B -> null
delete B without head
此时既找不到 A,也没有 C 可借,所以无法用 O(1) 删除尾节点。除非你有 head 从头遍历找前驱,或者链表是双向链表。
这就是这个面试题一定会带条件“给定节点不是尾节点”的原因。
三、覆盖删除的具体过程
链表:
1 -> 2 -> 3 -> 4
delete node value 2
操作:
node.val = node.next.val;
node.next = node.next.next;
过程图:
before:
1 -> [2] -> [3] -> 4
copy:
1 -> [3] -> [3] -> 4
skip next:
1 -> [3] -> 4
逻辑上值为 2 的节点消失了,但物理上原来的 node 还在,只是内容变成了下一个节点的内容。
四、这个技巧删除的是“值语义”
很多面试答案会说“删除当前节点”,但更准确是:把当前节点伪装成下一个节点,再删除下一个节点。
如果外部还有变量引用原来的下一个节点,它会发现那个节点被绕过去了。如果外部关心节点对象身份,这个技巧就可能不符合语义。
| 场景 | 是否适合 |
|---|---|
| LeetCode 风格只看链表值序列 | 适合 |
| 节点对象有唯一 ID 或外部引用 | 谨慎 |
| 节点持有文件句柄等资源 | 不适合直接复制 |
| 尾节点 | 不适合 |
面试里能说出这个边界,会比只背代码更稳。
五、复杂字段要考虑复制成本
如果节点只存一个整数,复制很轻。但如果节点里有多个字段、引用对象、统计信息,直接复制 val 可能不够。
node.val = node.next.val;
node.payload = node.next.payload;
node.next = node.next.next;
如果 payload 是可变对象引用,还会牵涉浅拷贝和深拷贝语义。真实工程里删除节点通常还是通过前驱或容器接口完成,不会依赖这种技巧。
六、和双向链表删除对比
双向链表节点有 prev 和 next,给定节点就能 O(1) 删除。
node.prev.next = node.next
node.next.prev = node.prev
这也是双向链表多一个前驱指针的价值:删除当前节点不需要从 head 找前驱。但代价是每个节点多一个指针,内存和维护成本更高。
七、常见误区与追问
- 误区:只给任意节点都能删除。 只有非尾节点才能用后继覆盖法,尾节点不行。
- 误区:这个方法真的删除了当前物理节点。 物理上删除的是下一个节点,当前节点只是被覆盖。
- 误区:复制一个 val 永远安全。 如果节点字段复杂或有对象身份语义,复制可能不符合业务要求。
- 追问:为什么单链表删除需要前驱? 因为要修改前驱的
next指针,让它跳过目标节点。 - 追问:双向链表为什么能 O(1) 删除给定节点? 节点自带
prev,能直接改前后两个指针。 - 追问:这个技巧时间和空间复杂度是多少? 时间 O(1),额外空间 O(1),前提是非尾节点且复制成本视为常数。
八、加强记忆
这题记成“没有前驱,借后继”。单链表正常删除要改前驱的 next,只给当前节点时找不到前驱;如果当前节点不是尾节点,就把后继内容复制过来,再跳过后继。它适合面试题里的值序列语义,不适合尾节点、复杂资源对象和强节点身份场景。