题目描述
给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。
解题思路
- 双指针技巧:
- 快指针先移动 N 步
- 慢指针从头开始同步移动
- 虚拟头节点:
- 创建哑节点简化头节点删除操作
- 始终返回 dummy.next 作为结果
- 同步移动:
- 快慢指针同步移动直至快指针到链表尾
- 此时慢指针指向待删除节点的前驱
- 删除操作:
- 修改慢指针的 next 指向下下节点
- 跳过需删除节点完成移除
关键洞察
- 倒数位置转换:
- 倒数第 N 个 ≡ 正数第 (L-N+1) 个
- 双指针避免显式计算链表长度 L
- 间距控制原理:
- 快指针领先 N 步形成固定间距
- 当快指针到尾部时,慢指针恰在目标前驱
- 边界处理:
- 虚拟头节点统一处理头节点删除
- 处理 N>L 的情况(快指针提前到 null)
- 一次遍历:
- 仅需单次遍历即可完成删除
- 时间复杂度严格 O(L)
复杂度分析
| 指标 | 说明 |
|---|---|
| 时间复杂度 | O(L):L 为链表长度,只需一次遍历 |
| 空间复杂度 | O(1):仅使用常数空间存储指针 |
| 最佳情况 | O(1):当删除头节点时 |
代码实现
/**
* Definition for singly-linked list.
* function ListNode(val, next) {
* this.val = (val===undefined ? 0 : val)
* this.next = (next===undefined ? null : next)
* }
*/
/**
* @param {ListNode} head
* @param {number} n
* @return {ListNode}
*/
var removeNthFromEnd = function (head, n) {
const dummy = new ListNode(0, head); // 创建虚拟头节点,指向head
let fast = dummy, slow = dummy;
// 快指针先走 n+1 步
for (let i = 0; i <= n; i++) {
fast = fast.next;
}
// 快慢指针同时移动,直到快指针到达链表末尾
while (fast) {
fast = fast.next;
slow = slow.next;
}
// 删除倒数第n个节点
slow.next = slow.next.next;
return dummy.next; // 返回真正的头节点
};
实际应用场景
- 操作系统:内核调度中移除优先级最低的任务`
- 数据库管理:查询结果集中删除指定排名的记录`
- 实时监控:性能监控系统中移除响应最慢的节点`
- 游戏开发:玩家排行榜中移除指定名次的玩家`