提示 给你一个链表,删除链表的倒数第 n
个结点,并且返回链表的头结点。
示例 1:

ini
输入: head = [1,2,3,4,5], n = 2
输出: [1,2,3,5]
示例 2:
ini
输入: head = [1], n = 1
输出: []
示例 3:
ini
输入: head = [1,2], n = 1
输出: [1]
提示:
- 链表中结点的数目为
sz
1 <= sz <= 30
0 <= Node.val <= 100
1 <= n <= sz
进阶: 你能尝试使用一趟扫描实现吗?
解答
核心思路:快慢双指针法
--->使用两个指针(快慢指针),让快指针先走 N 步,然后两个指针同步移动。当快指针到达尾部时,慢指针正好指向倒数第 N 个节点的前驱节点,完成删除操作。
ini
class ListNode {
int val;
ListNode next;
ListNode() {}
ListNode(int val) { this.val = val; }
ListNode(int val, ListNode next) { this.val = val; this.next = next; }
}
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
// 1. 创建哑节点(哨兵节点),用于处理头节点删除情况
ListNode dummy = new ListNode(0);
dummy.next = head;
// 2. 初始化快慢指针,都从哑节点开始
ListNode fast = dummy;
ListNode slow = dummy;
// 3. 快指针先走 N 步
for (int i = 0; i <= n; i++) {
fast = fast.next;
}
// 4. 同步移动快慢指针
while (fast != null) {
fast = fast.next;
slow = slow.next;
}
// 5. 删除倒数第 N 个节点
slow.next = slow.next.next;
// 6. 返回新头节点(始终是 dummy.next)
return dummy.next;
}
}
示例说明(以输入head = [1,2,3,4,5], n=2
为例)
- 创建哑节点 :
dummy(0) -> 1 -> 2 -> 3 -> 4 -> 5
- 初始化指针 :
fast
和slow
都指向dummy(0)
- 快指针移动n+1=3步 :
- 第一次:
fast=1
- 第二次:
fast=2
- 第三次:
fast=3
- 第一次:
- 同时移动 :
- 快指针从3开始移动到尾部(4,5,null),慢指针从0开始移动到1,2,3。当快指针为
null
时,慢指针在3的位置
- 快指针从3开始移动到尾部(4,5,null),慢指针从0开始移动到1,2,3。当快指针为
- 删除节点 :
slow(3)
的下一个节点是4(即倒数第2个节点),修改其next
指向5,链表变为dummy(0)->1->2->3->5
- 返回 :
dummy.next=1
,即返回[1,2,3,5]

完整算法步骤
- 创建哑节点 :
- 新建一个哑节点
dummy
,并将其next
指向链表的头节点。
- 新建一个哑节点
- 初始化两个指针 :
fast
(快指针)和slow
(慢指针),都指向哑节点。
- 快指针先移动N+1步 :
- 这样快指针和慢指针之间就保持了N+1个节点的距离。当快指针移动到链表末尾时,慢指针刚好指向待删除节点的前驱节点。
- 同时移动快指针和慢指针 :
- 当快指针不为
null
时,快慢指针同时向后移动一步,直到快指针指向null
(即快指针到达链表尾部)。
- 当快指针不为
- 删除节点 :
- 此时慢指针
slow
指向待删除节点的前一个节点,将slow.next
指向slow.next.next
,就删除了倒数第N个节点。
- 此时慢指针
- 返回新链表的头节点 :
- 返回哑节点
dummy
的next
,即新链表的头节点。
- 返回哑节点