LeetCode 237. 删除链表中的节点
题目描述



题意分析
只提供链表中待删除的节点
node,无法访问头节点,也没有它的前驱。题目保证节点值互不相同,且node一定不是尾节点。删除后的链表应少一个节点,原目标值不再出现,其他值仍按原顺序排列。这里关注的是链表可见的值序列,不要求传入的那个节点对象本身必须脱离链表;“不是尾节点”正是完成这种删除的关键条件。
解法:复制后继节点并跳过
核心思路
[!blue]
普通删除要让前驱指向当前节点的后继,但这里只知道当前节点,单链表又不能向前走,所以无法直接修改前驱连接。不过当前节点的后继一定存在,且它的值和后继指针都可访问。
让当前节点接替它的后继:先把后继的值复制到当前节点,再让当前节点跳过原后继,直接指向后继的下一节点。这样原目标值被覆盖,原后继值通过当前节点保留下来,后面的所有值仍维持原顺序。
当前节点之前的连接无需改变,实际脱链的是原后继对象。链表长度减少一,除目标值外的各个值按原顺序保留,便满足题目要求。
先复制值、后修改连接的顺序不能颠倒,否则
node.next已经不再是原后继。若允许删除尾节点,就既没有后继可接替,又没有前驱可修改,因此当前接口无法使用这套方法;题目已明确排除了这种输入。
解题步骤
- 执行
node.val = node.next.val,用原后继值覆盖目标值。- 执行
node.next = node.next.next,把原后继从链中跳过。- 直接结束函数,修改的是共享节点对象,无需返回或重新赋值头节点。
代码实现
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只改变函数里的引用,不会修改调用方链表中的节点或连接。- 对尾节点套用方法会访问空后继,该情况不在题目保证的输入范围内。
- 不能声称传入节点对象已被物理移除,它仍然留在链表中并承载原后继的值。
转载与许可
许可
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!