链表操作:删除倒数第N个节点的快慢指针解法
1. 链表操作基础与问题定义链表作为数据结构中的经典类型其操作一直是算法面试的高频考点。今天我们要解决的删除链表的倒数第 N 个节点问题看似简单却暗藏多个技术要点。这个问题在LeetCode上编号为19属于链表类问题的中等难度题目但正确率却只有36.7%说明其中存在不少容易踩坑的细节。1.1 链表结构回顾单链表由一系列节点组成每个节点包含两个部分数据域存储元素值指针域存储下一个节点的地址class ListNode: def __init__(self, val0, nextNone): self.val val self.next next与数组不同链表不需要连续的内存空间插入删除操作的时间复杂度为O(1)但随机访问的效率是O(n)。这种特性使得链表特别适合频繁增删的场景。1.2 问题具体描述给定一个链表删除倒数第n个节点并返回头节点。例如 输入1-2-3-4-5, n2 输出1-2-3-5这里有几个关键约束条件需要注意链表长度可能很大LeetCode测试用例中最多可达30个节点n保证是有效值不会大于链表长度需要处理头节点被删除的特殊情况提示在实际面试中一定要先确认这些边界条件很多同学失败就是因为忽略了头节点删除的情况。2. 解决方案设计与比较2.1 暴力解法两次遍历最直观的思路是先遍历链表获取长度L再第二次遍历到L-n的位置进行删除。这种方法时间复杂度O(2n)O(n)空间复杂度O(1)。def removeNthFromEnd(head, n): dummy ListNode(0, head) length 0 curr head while curr: length 1 curr curr.next curr dummy for _ in range(length - n): curr curr.next curr.next curr.next.next return dummy.next虽然这种方法可行但在面试中通常会被要求优化为一次遍历这就引出了经典的快慢指针技巧。2.2 最优解快慢指针法快慢指针是解决链表问题的利器其核心思想是快指针先走n步然后快慢指针同步前进当快指针到达末尾时慢指针正好指向要删除节点的前驱def removeNthFromEnd(head, n): dummy ListNode(0, head) fast slow dummy # 快指针先走n步 for _ in range(n): fast fast.next # 同步移动直到快指针到末尾 while fast.next: fast fast.next slow slow.next # 删除节点 slow.next slow.next.next return dummy.next这种方法的时间复杂度为O(n)空间复杂度O(1)是最优解。使用dummy节点的技巧避免了处理头节点删除的特殊情况。3. 关键实现细节与调试技巧3.1 dummy节点的妙用dummy节点哨兵节点是链表问题中的常用技巧它有三大优势统一处理头节点和其他节点的删除逻辑避免空指针异常如链表长度为1时简化边界条件判断在代码中我们创建dummy节点并让它指向headdummy ListNode(0, head)这样即使要删除的是头节点我们也能通过dummy.next安全地返回新的头节点。3.2 指针移动的步数控制快指针先走n步的实现需要注意for _ in range(n): fast fast.next这里容易犯的错误是让快指针从head而不是dummy开始会导致少走一步循环条件写成range(n1)会导致多走一步调试技巧可以在纸上画出n2时的指针移动过程验证快指针是否停在正确位置。3.3 循环终止条件同步移动时的终止条件是关键while fast.next: fast fast.next slow slow.next这个条件确保当fast指向最后一个节点时停止此时slow指向要删除节点的前驱。如果写成while fastslow会多走一步导致删除错误节点。4. 常见错误与测试用例设计4.1 典型错误模式分析根据LeetCode提交统计最常见的错误包括空指针异常占错误提交的43%未处理链表长度为1的情况未考虑删除头节点的情况删除错误节点32%快指针多走或少走一步循环终止条件错误内存泄漏15%Python中虽然不需要手动释放内存但在C等语言中需要返回值错误10%忘记通过dummy.next返回新头节点4.2 必备测试用例集完整的测试应该包含以下情况常规情况输入[1,2,3,4,5], n2 → 输出[1,2,3,5]删除头节点输入[1,2], n2 → 输出[2]删除尾节点输入[1,2,3], n1 → 输出[1,2]单节点链表输入[1], n1 → 输出[]大n值输入[1,2,3,4,5], n5 → 输出[2,3,4,5]4.3 调试打印技巧在开发过程中可以添加打印函数辅助调试def print_list(head): while head: print(head.val, end - ) head head.next print(None) # 在关键步骤后调用 print(After moving fast:) print_list(fast)5. 算法扩展与变种问题5.1 相似题目推荐掌握了这道题后可以尝试以下变种删除链表中间节点LeetCode 876先找中间节点旋转链表LeetCode 61交换相邻节点LeetCode 24回文链表LeetCode 2345.2 实际应用场景这种快慢指针技巧在以下场景中有实际应用检测链表环LeetCode 141寻找链表交点LeetCode 160内存管理中的垃圾回收算法网络协议中的超时检测机制5.3 多语言实现对比虽然我们以Python为例但其他语言的实现也值得了解C版本注意手动内存管理ListNode* removeNthFromEnd(ListNode* head, int n) { ListNode* dummy new ListNode(0, head); ListNode *fast dummy, *slow dummy; for(int i0; in; i) fast fast-next; while(fast-next) { fast fast-next; slow slow-next; } ListNode* toDelete slow-next; slow-next slow-next-next; delete toDelete; // 避免内存泄漏 ListNode* newHead dummy-next; delete dummy; return newHead; }Java版本垃圾回收自动处理public ListNode removeNthFromEnd(ListNode head, int n) { ListNode dummy new ListNode(0, head); ListNode fast dummy, slow dummy; for(int i0; in; i) fast fast.next; while(fast.next ! null) { fast fast.next; slow slow.next; } slow.next slow.next.next; return dummy.next; }6. 性能优化与进阶思考6.1 时间复杂度分析虽然快慢指针法已经是O(n)时间复杂度但在实际工程中还可以考虑并行遍历理论上可以将链表分段用多线程同时统计各部分长度缓存长度如果链表会被频繁查询可以维护一个长度计数器跳表优化将单链表改造成跳表结构可以加速定位过程6.2 内存优化技巧对于内存敏感的环境复用节点某些语言中对象创建开销大可以考虑对象池紧凑存储如果节点值类型相同可以使用内存连续的结构延迟删除标记节点为逻辑删除批量处理物理删除6.3 不可变链表实现在函数式编程中链表通常是不可变的。这时删除操作需要返回新链表def removeNthFromEndImmutable(head, n): def helper(node, acc): if not node: return acc, 0 new_tail, count helper(node.next, acc) new_count count 1 if new_count n: return new_tail, new_count new_node ListNode(node.val, new_tail) return new_node, new_count new_head, _ helper(head, None) return new_head这种实现虽然空间复杂度较高O(n)递归栈但符合函数式编程原则。

相关新闻

AI编程时代的技术债:如何为AI生成代码构建质量保障与责任体系

AI编程时代的技术债:如何为AI生成代码构建质量保障与责任体系

1. 从“AI写完了功能”到“凌晨两点的报警”:一个真实的故事凌晨两点,手机屏幕在黑暗中骤然亮起,刺耳的警报声划破寂静。你挣扎着从床上爬起来,睡眼惺忪地打开电脑,登录监控系统,一行行红色的错误日志映入眼…

2026/10/17 8:48:26 阅读更多 →
5个核心模块彻底掌握ComfyUI中文工作流:从新手到AI创作专家的完整指南

5个核心模块彻底掌握ComfyUI中文工作流:从新手到AI创作专家的完整指南

5个核心模块彻底掌握ComfyUI中文工作流:从新手到AI创作专家的完整指南 【免费下载链接】ComfyUI-Workflows-ZHO 我的 ComfyUI 工作流合集 | My ComfyUI workflows collection 项目地址: https://gitcode.com/GitHub_Trending/co/ComfyUI-Workflows-ZHO 你是否…

2026/10/17 0:22:42 阅读更多 →
Web3.js与OKX钱包集成指南:构建DApp连接层的核心实践

Web3.js与OKX钱包集成指南:构建DApp连接层的核心实践

1. 项目概述:为什么我们需要连接钱包? 如果你正在开发一个去中心化应用(DApp),那么与用户的钱包进行交互,就是整个应用逻辑的起点和核心。这就像传统互联网应用需要用户登录一样,在Web3的世界里…

2026/10/17 8:51:30 阅读更多 →

最新新闻

Netty实战进阶:粘包半包、EventLoop线程边界与长连接心跳

Netty实战进阶:粘包半包、EventLoop线程边界与长连接心跳

不少人可能和我有同样的感受:Netty的入门教程看了一遍又一遍,Echo服务器、聊天室Demo敲得飞起,但一到真实项目里要“实战应用”了,反而手足无措。客户端发过来的数据一多,消息全黏在一起;连接一挂&#xff…

2026/10/18 7:17:00 阅读更多 →
扫码点餐小程序多门店源码跑通指南

扫码点餐小程序多门店源码跑通指南

简介:这是一套基于Java后端与uniapp(Vue3)前端实现的在线点餐小程序完整源码,面向计算机、电子信息等专业学生及初级全栈开发者,适用于课程设计、期末大作业或毕业设计参考。系统支持扫码点餐、外卖配送与到店自取双模…

2026/10/18 7:17:00 阅读更多 →
新能源汽车实训仿真教学软件选型与课堂实施经验

新能源汽车实训仿真教学软件选型与课堂实施经验

做职业教育这行的人,多半都有过一种尴尬:理论课讲得头头是道,学生一到实训车跟前就手足无措。尤其是新能源汽车实训,教材上写的、课件里放的、实车上摸的,三者经常对不上。前几年我们引了一套以比亚迪秦EV为原型的电动…

2026/10/18 7:17:00 阅读更多 →
结合AI客户端玩转go MCP项目开发:TaoToken统一Key接入实战

结合AI客户端玩转go MCP项目开发:TaoToken统一Key接入实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/18 7:17:00 阅读更多 →
鸿蒙编码与安全双翼:中小企业智能化改造的落地指南

鸿蒙编码与安全双翼:中小企业智能化改造的落地指南

前几天一位做设备运维的中小企业负责人找我,说客户要求巡检数据必须可追溯、不能外泄,而他们过去的办法是拍照发到工作群。这个场景我最近半年至少遇到过五次,几乎已经成为中小企业在智能化改造里最典型的痛点。很多人一听到“鸿蒙编码”和“…

2026/10/18 7:17:00 阅读更多 →
返利系统架构设计:订单归集、分润与对账全链路解析

返利系统架构设计:订单归集、分润与对账全链路解析

做返利类APP这事,外行看着简单——不就是把商品链接套上推广参数丢给用户,等平台结算佣金再返一部分出去吗?真上手你会知道,开发量最大的根本不是前端页面,而是后端的订单归集、分润和对账三块。这三块任何一个环节出问…

2026/10/18 7:16:00 阅读更多 →

日新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/18 0:00:55 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/18 0:00:55 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/18 0:00:55 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/18 0:00:55 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/18 0:00:55 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/18 0:00:55 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/18 0:55:02 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/17 17:22:31 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 14:23:36 阅读更多 →