LeetCode 第19题:删除链表的倒数第N个结点
1.LeetCode 第10题:正则表达式匹配2.LeetCode 第1题:两数之和3.LeetCode 第2题:两数相加4.LeetCode 第3题:无重复字符的最长子串5.LeetCode 第4题:寻找两个正序数组的中位数6.LeetCode 第8题:字符串转换整数 (atoi)7.LeetCode 第7题:整数反转8.LeetCode 第6题:Z字形变换9.LeetCode 第5题:最长回文子串10.LeetCode 第9题:回文数11.LeetCode 第11题:盛最多水的容器12.LeetCode 第12题:整数转罗马数字13.LeetCode 第13题:罗马数字转整数14.LeetCode 第14题:最长公共前缀15.LeetCode 第15题:三数之和16.LeetCode 第16题:最接近的三数之和17.LeetCode 第17题:电话号码的字母组合18.LeetCode 第18题:四数之和
19.LeetCode 第19题:删除链表的倒数第N个结点
20.LeetCode 第20题:有效的括号21.LeetCode 第21题:合并两个有序链表22.LeetCode 第22题:括号生成23.LeetCode 第23题:合并K个升序链表24.LeetCode 第24题:两两交换链表中的节点25.LeetCode 第25题:K 个一组翻转链表26.LeetCode 第26题:删除有序数组中的重复项27.LeetCode 第27题:移除元素28.LeetCode 第28题:找出字符串中第一个匹配项的下标29.LeetCode 第29题:两数相除30.LeetCode 第30题:串联所有单词的子串31.LeetCode 第31题:下一个排列32.LeetCode 第32题:最长有效括号33.LeetCode 第33题:搜索旋转排序数组34.LeetCode 第34题:在排序数组中查找元素的第一个和最后一个位置35.LeetCode 第35题:搜索插入位置36.LeetCode 第36题:有效的数独37.LeetCode 第37题:解数独38.LeetCode 第38题:外观数列39.LeetCode 第39题:组合总和40.LeetCode 第40题:组合总和 II41.LeetCode 第41题:缺失的第一个正数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
总结与反思
- 这道题的关键点:
- 使用虚拟头节点简化操作
- 双指针技巧的应用
- 边界情况的处理
- 两种解法比较:
- 双指针:一趟扫描,更优雅
- 两趟扫描:思路直观,但效率较低
- 优化思路:
- 使用虚拟头节点
- 一趟扫描实现
- 注意边界条件
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了