题目描述

✅ 237. 删除链表中的节点

image-20260928235327133

image-20260928235327134

image-20260928235327136

题意分析

只提供链表中待删除的节点 node,无法访问头节点,也没有它的前驱。题目保证节点值互不相同,且 node 一定不是尾节点。

删除后的链表应少一个节点,原目标值不再出现,其他值仍按原顺序排列。这里关注的是链表可见的值序列,不要求传入的那个节点对象本身必须脱离链表;“不是尾节点”正是完成这种删除的关键条件。

解法:复制后继节点并跳过

核心思路

[!blue]

普通删除要让前驱指向当前节点的后继,但这里只知道当前节点,单链表又不能向前走,所以无法直接修改前驱连接。不过当前节点的后继一定存在,且它的值和后继指针都可访问。

让当前节点接替它的后继:先把后继的值复制到当前节点,再让当前节点跳过原后继,直接指向后继的下一节点。这样原目标值被覆盖,原后继值通过当前节点保留下来,后面的所有值仍维持原顺序。

当前节点之前的连接无需改变,实际脱链的是原后继对象。链表长度减少一,除目标值外的各个值按原顺序保留,便满足题目要求。

先复制值、后修改连接的顺序不能颠倒,否则 node.next 已经不再是原后继。若允许删除尾节点,就既没有后继可接替,又没有前驱可修改,因此当前接口无法使用这套方法;题目已明确排除了这种输入。

解题步骤

  1. 执行 node.val = node.next.val,用原后继值覆盖目标值。
  2. 执行 node.next = node.next.next,把原后继从链中跳过。
  3. 直接结束函数,修改的是共享节点对象,无需返回或重新赋值头节点。

代码实现

class Solution {
    public void deleteNode(ListNode node) {
        // 后继保证存在;先复制再跳过,删除目标值序列时实际脱链的是后继。
        node.val = node.next.val;
        node.next = node.next.next;
    }
}
func deleteNode(node *ListNode) {
    // 后继保证存在;先复制再跳过,删除目标值序列时实际脱链的是后继。
    node.Val = node.Next.Val
    node.Next = node.Next.Next
}

复杂度分析

  • 时间复杂度:$O(1)$,只读取和修改相邻节点的固定几个字段。
  • 空间复杂度:$O(1)$,不遍历链表,也不创建新节点。

关键点总结

[!green]

  • 无法修改前驱,就让当前节点顶替后继,再跳过后继。
  • 删除的是目标值在序列中的出现,实际脱链对象是原后继。
  • 非尾节点保证后继存在,先复制再跳过保证读取的仍是原后继值。

易错点总结

[!yellow]

  • 只复制值不跳过后继,链表长度不会减少,还会保留两个相同值。
  • 只跳过后继不复制值,消失的是后继值,目标值反而仍在链上。
  • 直接令局部变量 node = node.next 只改变函数里的引用,不会修改调用方链表中的节点或连接。
  • 对尾节点套用方法会访问空后继,该情况不在题目保证的输入范围内。
  • 不能声称传入节点对象已被物理移除,它仍然留在链表中并承载原后继的值。
转载与许可
作者
链接 https://hgnulb.github.io/blog/2026/36392596
许可 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!