
js
var removeNthFromEnd = function (head, n) {
// 1. 创建哨兵(dummy)节点,值为 0,next 指向原链表头节点
// 作用:统一处理「删除头节点」和「删除中间节点」的逻辑,避免边界判断
const dummy = new ListNode(0, head)
// 2. 初始化快慢指针(left 慢指针,right 快指针),都指向哨兵节点
let left = dummy
let right = dummy
// 3. 先让快指针(right)向右移动 n 步
// 此时快慢指针之间的距离为 n,为后续找倒数第 n 个节点做准备
while (n--) {
right = right.next // 每循环一次,n 减 1,直到 n 为 0 停止
}
// 4. 快慢指针同时向右移动,直到快指针走到链表末尾(right.next 为 null)
// 此时慢指针(left)的下一个节点就是「倒数第 n 个节点」
while (right.next) {
left = left.next // 慢指针走一步
right = right.next // 快指针走一步
}
// 5. 删除倒数第 n 个节点:将慢指针的 next 指向其下下个节点
// 跳过要删除的节点,链表中该节点会被垃圾回收
left.next = left.next.next
// 6. 返回新链表的头节点(哨兵节点的 next)
// 避免原头节点被删除后返回 null 的情况
return dummy.next
}dummy的作用
dummy 哨兵节点的核心作用是统一处理链表头部被删除的特殊情况,让删除逻辑对链表中所有节点(包括头节点)都保持一致,避免额外的条件判断。
场景 1:不使用 dummy 节点的问题
假设链表是 [1](只有一个节点),要删除倒数第 1 个节点(也就是头节点):
- 没有
dummy时,删除头节点需要直接修改head为null; - 如果链表是
[1,2,3],要删除倒数第 3 个节点(也是头节点),同样需要修改head为head.next; - 但如果删除的是中间节点(比如倒数第 2 个节点 2),只需要让
1.next = 3即可。
这就导致删除头节点和删除中间 / 尾节点的逻辑不一致,你需要额外写 if 语句判断是否删除的是头节点,代码会更复杂且容易出错。