- Published on
LeetCode-19.删除链表的倒数第 N 个结点
- Authors
- Name
- Piggy DP
- @xiaozhudxiaozhu
声明:该文通过我个人写Prompt喂给AI产出
问题描述
给定一个单链表 head
和一个整数 n
,删除链表中倒数第 n
个节点,并返回链表的头节点。
原题链接:https://leetcode.cn/problems/remove-nth-node-from-end-of-list/
双指针法
通过双指针找到链表中倒数第 n
个节点的前驱节点,然后调整指针以删除目标节点。
具体思路
双指针法的关键在于找到倒数第 n
个节点的前驱节点。具体分析如下:
- 使用虚拟头节点:
- 创建一个虚拟头节点
dummy
,并将其next
指向链表的头节点。这样可以方便处理特殊情况,比如删除的是头节点。
- 创建一个虚拟头节点
- 找到倒数第
n + 1
个节点:- 使用两个指针
p
和p1
,先让p
向前移动n
步。 - 然后同时移动
p
和p1
,直到p
到达链表末尾。此时p1
指向的就是倒数第n + 1
个节点。
- 使用两个指针
- 删除倒数第
n
个节点:- 找到倒数第
n + 1
个节点后,将其next
指针指向倒数第n - 1
个节点,从而跳过倒数第n
个节点。
- 找到倒数第
- 返回结果:
- 返回
dummy.next
,即修改后的链表头节点。
- 返回
公示推导
- 公式推导:
- 通过公式
k + n = l
,其中k
是从头节点到目标节点的距离,n
是从目标节点到链表末尾的距离,l
是链表的长度。 - 推导出
k = l - n
,即从头节点走k
步就能到达目标节点。
- 通过公式
- 双指针的核心思想:
- 先让一个指针
p
走n
步,此时它距离链表末尾还剩k
步。 - 然后同时移动
p
和另一个指针p1
,直到p
到达链表末尾(null
)。 - 此时
p1
正好走了k
步,指向倒数第n
个节点。
- 先让一个指针
- 删除目标节点:
- 找到倒数第
n + 1
个节点后,调整其next
指针,跳过倒数第n
个节点。
- 找到倒数第
代码实现
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
// 创建虚拟头节点,方便处理删除头节点的情况
ListNode dummy = new ListNode(-1);
dummy.next = head;
// 找到倒数第 n+1 个节点
ListNode node = findNthFromEnd(dummy, n + 1);
// 删除倒数第 n 个节点
node.next = node.next.next;
// 返回修改后的链表头节点
return dummy.next;
}
// 辅助方法:找到倒数第 n 个节点
public ListNode findNthFromEnd(ListNode head, int n) {
ListNode p = head;
// 先让 p 移动 n 步
for (int i = 0; i < n; i++) {
p = p.next;
}
// 同时移动 p 和 p1,直到 p 到达链表末尾
ListNode p1 = head;
while (p != null) {
p1 = p1.next;
p = p.next;
}
// 此时 p1 指向倒数第 n 个节点
return p1;
}
}
示例
链表: 1 -> 2 -> 3 -> 4 -> 5
n = 2
- 找到倒数第
n + 1
个节点3
。 - 删除倒数第
n
个节点4
。 - 修改后的链表为
1 -> 2 -> 3 -> 5
。
复杂度分析
- 时间复杂度:
O(n)
,其中n
是链表的长度。- 找到倒数第
n + 1
个节点需要O(n)
的时间。 - 删除节点需要
O(1)
的时间。
- 找到倒数第
- 空间复杂度:
O(1)
,只使用了常数级别的额外空间。