← 返回题目列表

只给链表中的某个节点,如何删除这个节点?

高频 中等 第 15 / 28 题 更新于 2026/07/29
链表删除节点指针操作

简化版

如果只给要删除的节点 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 是可变对象引用,还会牵涉浅拷贝和深拷贝语义。真实工程里删除节点通常还是通过前驱或容器接口完成,不会依赖这种技巧。

六、和双向链表删除对比

双向链表节点有 prevnext,给定节点就能 O(1) 删除。

node.prev.next = node.next
node.next.prev = node.prev

这也是双向链表多一个前驱指针的价值:删除当前节点不需要从 head 找前驱。但代价是每个节点多一个指针,内存和维护成本更高。

七、常见误区与追问

  • 误区:只给任意节点都能删除。 只有非尾节点才能用后继覆盖法,尾节点不行。
  • 误区:这个方法真的删除了当前物理节点。 物理上删除的是下一个节点,当前节点只是被覆盖。
  • 误区:复制一个 val 永远安全。 如果节点字段复杂或有对象身份语义,复制可能不符合业务要求。
  • 追问:为什么单链表删除需要前驱? 因为要修改前驱的 next 指针,让它跳过目标节点。
  • 追问:双向链表为什么能 O(1) 删除给定节点? 节点自带 prev,能直接改前后两个指针。
  • 追问:这个技巧时间和空间复杂度是多少? 时间 O(1),额外空间 O(1),前提是非尾节点且复制成本视为常数。

八、加强记忆

这题记成“没有前驱,借后继”。单链表正常删除要改前驱的 next,只给当前节点时找不到前驱;如果当前节点不是尾节点,就把后继内容复制过来,再跳过后继。它适合面试题里的值序列语义,不适合尾节点、复杂资源对象和强节点身份场景。