LeetCode 第19题:删除链表的倒数第N个结点
LeetCode 第19题:删除链表的倒数第N个结点
题目描述
给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。
难度
中等
题目链接
https://leetcode.cn/problems/remove-nth-node-from-end-of-list/
示例
示例 1:
输入:head = [1,2,3,4,5], n = 2
输出:[1,2,3,5]
示例 2:
输入:head = [1], n = 1
输出:[]
示例 3:
输入:head = [1,2], n = 1
输出:[1]
提示
- 链表中结点的数目为 sz
- 1 <= sz <= 30
- 0 <= Node.val <= 100
- 1 <= n <= sz
解题思路
方法一:双指针(一趟扫描)
这道题可以使用双指针(快慢指针)来实现一趟扫描。
关键点:
- 使用快慢指针,快指针先走n步
- 使用虚拟头节点简化边界情况
- 当快指针到达末尾时,慢指针的下一个节点就是要删除的节点
具体步骤:
- 创建虚拟头节点,简化边界情况
- 快指针先走n步
- 快慢指针同时移动,直到快指针到达末尾
- 删除慢指针的下一个节点
- 返回虚拟头节点的下一个节点
时间复杂度:O(n),其中n是链表长度
空间复杂度:O(1)
方法二:计算长度(两趟扫描)
先遍历一遍计算链表长度,再遍历一遍删除指定节点。
具体步骤:
- 第一次遍历计算链表长度length
- 计算正向位置:pos = length - n
- 第二次遍历到pos位置删除节点
代码实现
C# 实现(双指针)
/**
* Definition for singly-linked list.
* public class ListNode {
* public int val;
* public ListNode next;
* public ListNode(int val=0, ListNode next=null) {
* this.val = val;
* this.next = next;
* }
* }
*/
public class Solution {
public ListNode RemoveNthFromEnd(ListNode head, int n) {
// 创建虚拟头节点
ListNode dummy = new ListNode(0, head);
ListNode fast = dummy;
ListNode slow = dummy;
// 快指针先走n步
for (int i = 0; i < n; i++) {
fast = fast.next;
}
// 快慢指针同时移动
while (fast.next != null) {
fast = fast.next;
slow = slow.next;
}
// 删除目标节点
slow.next = slow.next.next;
return dummy.next;
}
}
C# 实现(两趟扫描)
public class Solution {
public ListNode RemoveNthFromEnd(ListNode head, int n) {
// 创建虚拟头节点
ListNode dummy = new ListNode(0, head);
// 计算链表长度
int length = 0;
ListNode current = head;
while (current != null) {
length++;
current = current.next;
}
// 找到要删除节点的前一个节点
current = dummy;
for (int i = 0; i < length - n; i++) {
current = current.next;
}
// 删除目标节点
current.next = current.next.next;
return dummy.next;
}
}
代码详解
双指针版本:
- 虚拟头节点:
- 使用dummy节点简化边界情况
- 特别是当需要删除第一个节点时
- 快指针移动:
- 先走n步建立距离
- 保持这个距离同时移动
- 删除节点:
- slow.next指向slow.next.next
- 完成节点删除操作
两趟扫描版本:
- 计算长度:
- 遍历一遍得到总长度
- 计算正向位置
- 删除节点:
- 移动到目标位置的前一个节点
- 修改next指针完成删除
执行结果
双指针版本:
- 执行用时:84 ms
- 内存消耗:37.9 MB
两趟扫描版本:
- 执行用时:88 ms
- 内存消耗:38.1 MB
总结与反思
- 这道题的关键点:
- 使用虚拟头节点简化操作
- 双指针技巧的应用
- 边界情况的处理
- 两种解法比较:
- 双指针:一趟扫描,更优雅
- 两趟扫描:思路直观,但效率较低
- 优化思路:
- 使用虚拟头节点
- 一趟扫描实现
- 注意边界条件

浙公网安备 33010602011771号