新闻详情

新闻详情

首页 / 资讯中心 / 详情

LeetCode 24 两两交换链表中的节点(Swap Nodes in Pairs)全解:数组转换、递归与原地迭代三种解法

发布时间:2026/9/19 5:13:54来源:尧图网络
LeetCode 24 两两交换链表中的节点(Swap Nodes in Pairs)全解:数组转换、递归与原地迭代三种解法
LeetCode 24 两两交换链表中的节点Swap Nodes in Pairs全解数组转换、递归与原地迭代三种解法【免费下载链接】leetcodeLeetcode solutions项目地址: https://gitcode.com/GitHub_Trending/leetcode1/leetcode本文围绕 LeetCode 24「两两交换链表中的节点Swap Nodes in Pairs」展开完整讲解数组转换、递归、原地迭代三种解法的直觉、算法步骤、多语言实现与复杂度分析并对照本仓库leetcode1/leetcode中python/0024-swap-nodes-in-pairs.py、java/0024-swap-nodes-in-pairs.java、go/0024-swap-nodes-in-pairs.go等真实提交源码深入剖析指针操作细节与常见陷阱。读完本文你将掌握链表两两交换的核心套路dummy 哨兵节点、保留下一对引用、奇数长度边界处理并能熟练迁移到其它链表重排类问题。问题回顾与前置知识题目要求给定一个单链表将每两个相邻节点交换位置并返回新链表的头节点。注意约束是只能修改节点的next指针不能修改节点的val值。例如输入head [1,2,3,4]输出应为[2,1,4,3]此示例见 c/0024-swap-nodes-in-pairs.c 头部注释。在动手之前需要具备以下基础链表基础Linked List Fundamentals理解单链表节点结构valnext、遍历方式以及指针修改的语义。各语言的节点定义在仓库各实现文件顶部的注释中均有给出例如 Python 的ListNode(val0, nextNone)、Go 的type ListNode struct { Val int; Next *ListNode }。原地指针操作In-Place Pointer Manipulation通过改变next指针来重排节点不依赖额外数据结构。Dummy 节点技巧Dummy Node Technique使用哨兵节点简化「头节点变化」时的边界处理。当头节点可能被替换时dummy.next始终指向最终的新头避免了大量特判。方法一转换为数组Convert To Array直觉最朴素的想法链表无法按下标随机访问但数组可以。先把链表节点全部收进数组利用下标直接交换相邻元素再按新顺序把节点重新串起来。该方案以空间换实现简单适合对指针操作不熟练时快速验证思路。算法步骤遍历链表把所有节点依次存入数组。以步长2遍历数组交换相邻两个元素。遍历数组把每个节点的next指向数组中下一个节点。将最后一个节点的next置为null返回数组第一个元素作为新头。核心代码Python 示例class Solution: def swapPairs(self, head: Optional[ListNode]) - Optional[ListNode]: if not head: return None arr [] cur head while cur: arr.append(cur) cur cur.next for i in range(0, len(arr) - 1, 2): arr[i], arr[i 1] arr[i 1], arr[i] for i in range(len(arr) - 1): arr[i].next arr[i 1] arr[-1].next None return arr[0]该思路的 C、Java、JavaScript、C#、Go、Kotlin、Swift、Rust 版本在原文的::tabs-start代码块中均已给出各语言仅容器 API 不同vector/ArrayList/List/[]/Vec核心逻辑完全一致。实现细节提示交换的是节点引用而非节点值因此交换后仍需按数组顺序重建next链并务必把末尾节点的next置空否则可能残留指向旧后继的引用如原列表末尾节点本来next就是null时无碍但交换后的末尾节点可能是原倒数第二个节点。复杂度分析时间复杂度$O(n)$遍历链表一次、交换一次、重建连接一次。空间复杂度$O(n)$需要额外数组存放全部节点指针。方法二递归Recursion直觉两两交换天然具有递归结构先交换前两个节点剩余链表从第三个节点开始交给递归处理。设当前对为cur第一个节点与nxt第二个节点那么cur.next应指向「剩余链表两两交换后的新头」nxt.next指向curnxt成为这对的新头。递归结构天然适配任意长度链表。算法步骤基准情形Base Case链表为空或只有一个节点时直接返回head。保存当前对引用cur headnxt head.next。递归交换从第三个节点开始的子链表并将其结果接到cur.next。令nxt.next cur完成本对的反转。返回nxt作为本对交换后的新头。核心代码Python 示例class Solution: def swapPairs(self, head: Optional[ListNode]) - Optional[ListNode]: if not head or not head.next: return head cur head nxt head.next cur.next self.swapPairs(nxt.next) nxt.next cur return nxt仓库源码佐证本仓库go/0024-swap-nodes-in-pairs.go提交的正是递归版本且用 Go 的平行赋值一行完成指针重排func swapPairs(head *ListNode) *ListNode { if head nil || head.Next nil { return head } next : head.Next swapped : swapPairs(next.Next) next.Next, head.Next head, swapped return next }对比可见go版本的next.Next, head.Next head, swapped等价于 Python 版本中nxt.next cur; cur.next swapPairs(nxt.next)两步但需注意 Go 平行赋值会先取右侧值再统一赋值顺序上天然安全而在 Rust 中则必须借助OptionBoxListNode的take()显式取出所有权见原文 Rust 代码块中的cur.next.take()/nxt.next.take()用法。复杂度分析时间复杂度$O(n)$每个节点恰好被访问一次。空间复杂度$O(n)$递归深度为 $\frac{n}{2}$占用调用栈空间。方法三原地迭代Iteration直觉用循环原地交换核心是仔细管理指针。引入 dummy 哨兵节点简化头节点变化每处理一对需要保存下一对起始引用、反转当前对内部指针、把前驱接到交换后的新头。每次前进两个节点保证每对恰好处理一次。这是面试中最推荐的解法时间 $O(n)$、空间 $O(1)$。算法步骤创建dummy节点指向head初始化prev dummycurr head。当curr与curr.next均存在时循环保存下一对起点nxtPair curr.next.next。识别本对第二个节点second curr.next。反转本对second.next currcurr.next nxtPair。前驱指向新头prev.next second。前进prev currcurr nxtPair。返回dummy.next。核心代码Python 示例class Solution: def swapPairs(self, head: Optional[ListNode]) - Optional[ListNode]: dummy ListNode(0, head) prev, curr dummy, head while curr and curr.next: nxtPair curr.next.next second curr.next # Reverse this pair second.next curr curr.next nxtPair prev.next second # Update pointers prev curr curr nxtPair return dummy.next仓库源码中的两种迭代写法本仓库同一题存在两种等价的迭代实现恰好印证了「dummy 节点」与「直接记录新头」两种边界处理思路写法 Adummy 哨兵 prev/curr 双指针python/0024-swap-nodes-in-pairs.pyclass Solution: def swapPairs(self, head: ListNode) - ListNode: dummy ListNode(0, head) prev, curr dummy, head while curr and curr.next: nxtPair curr.next.next second curr.next second.next curr curr.next nxtPair prev.next second prev curr curr nxtPair return dummy.next写法 B不设 dummy用new_head单独记录新头c/0024-swap-nodes-in-pairs.c 与 cpp/0024-swap-nodes-in-pairs.cppstruct ListNode* swapPairs(struct ListNode* head) { if (head NULL || head-next NULL) return head; struct ListNode *new_head head-next; struct ListNode *prev NULL; while (head ! NULL head-next ! NULL) { struct ListNode *next_pair head-next-next; struct ListNode *second head-next; if (prev ! NULL) prev-next second; second-next head; head-next next_pair; prev head; head next_pair; } return new_head; }两种写法的差异仅在边界处理写法 B 在循环外先用new_head head-next锁定新头因为第一对的第二个节点必然是最终新头循环内用if (prev ! NULL)处理首对写法 A 则让dummy.next永远指向新头代码更统一、更不易出错。java/0024-swap-nodes-in-pairs.java中则同时保留了迭代dummy 版与递归两个版本供对照学习。复杂度分析时间复杂度$O(n)$单次遍历。空间复杂度$O(1)$仅使用常数个指针变量。常见陷阱Common Pitfalls陷阱一丢失下一对链表的引用交换当前对之前必须先把curr.next.next下一对起点保存下来。如果先做内部反转curr.next已被改写剩余链表将彻底丢失。正确做法每次循环开头立即执行nxtPair curr.next.next。陷阱二忘记更新前驱节点的next指针交换完一对之后前驱节点首对时为 dummy必须指向交换后的新头即second。常见错误是只把对内部两个节点反转正确却忘了把前驱接回链表导致链断裂、节点丢失。这正是引入prev或dummy指针的意义所在。陷阱三没有处理奇数长度链表当链表节点数为奇数时最后一个节点没有配对对象应保持原位不动。循环条件必须同时校验curr与curr.next都存在只检查其中一个会导致空指针异常如 Java/C/C 直接解引用null或末节点被错误处理。仓库中所有实现如 go/0024-swap-nodes-in-pairs.go 的head nil || head.Next nil都严格遵循了这一边界检查。三种解法对比总结方法思路时间复杂度空间复杂度适用场景转换为数组下标交换后重建链接$O(n)$$O(n)$快速验证思路、教学演示递归先交换前两个再递归处理剩余$O(n)$$O(n)$调用栈代码简洁、逻辑直观原地迭代dummy 指针反转$O(n)$$O(1)$面试与生产首选空间最优三种方法都满足题目「只能修改节点指针、不能改值」的约束。若想进一步巩固链表指针操作可继续练习本仓库中同族的题目reverse-a-linked-list.md、reverse-nodes-in-k-group.mdk2 时即本题、swap-nodes-in-pairs 的相邻变体 swapping-nodes-in-a-linked-list其中 java/1721-swapping-nodes-in-a-linked-list.java 等文件展示了另一种「交换节点值」而非「交换节点」的思路差异。掌握本题的 dummy 节点与指针保存技巧后这些进阶题都能迎刃而解。【免费下载链接】leetcodeLeetcode solutions项目地址: https://gitcode.com/GitHub_Trending/leetcode1/leetcode创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

Codebase Memory MCP:macOS本地代码记忆协议实战指南 2026/9/19 8:23:43

Codebase Memory MCP:macOS本地代码记忆协议实战指南

1. 项目概述:为什么 Codebase Memory MCP 是 macOS 开发者值得花两小时配置的“隐形助手”Codebase Memory MCP 不是一个独立应用,也不是某个大厂推出的明星产品——它本质上是一套轻量级、可嵌入的代码上下文记忆协议规范,专为本地化、隐私优…

阅读更多 →
MV3时代浏览器插件工程化:端侧AI与跨进程通信实战 2026/9/19 8:23:43

MV3时代浏览器插件工程化:端侧AI与跨进程通信实战

1. 当浏览器插件开始调度GPU、管理内存、调用本地模型:我们正在重写“扩展”的定义五年前,我给一个电商比价插件加个页面脚本注入,改几行 jQuery 就能抓价格、弹提示、自动填表单——那会儿我们管这叫“小脚本”,连构建工具都不配…

阅读更多 →
Flutter三方库all_english_words在鸿蒙生态中的离线词库实践 2026/9/19 8:23:43

Flutter三方库all_english_words在鸿蒙生态中的离线词库实践

1. 项目概述:Flutter三方库all_english_words的鸿蒙适配价值在鸿蒙生态中构建智能化的英文输入与教育应用时,开发者常面临一个基础性难题:如何在不依赖网络连接的情况下,快速获取海量英文词汇数据支持。这正是all_english_words库…

阅读更多 →
汽车出口数字化转型:ERP解决方案与供应链优化 2026/9/19 8:23:43

汽车出口数字化转型:ERP解决方案与供应链优化

1. 汽车出口行业的数字化转型机遇与挑战2024年上海市政府工作报告释放了一个明确信号:跨境电商和二手车出口等新业态将获得前所未有的政策支持。作为一名深耕外贸ERP领域多年的从业者,我亲眼见证了汽车出口企业在这波政策红利下的转型阵痛与突破。在这个…

阅读更多 →
大模型技术解析与学习路径全指南 2026/9/19 8:23:43

大模型技术解析与学习路径全指南

1. 大模型技术全景解析大模型(Large Language Model)作为当前人工智能领域最具突破性的技术之一,正在深刻改变人机交互方式。这类模型通常基于Transformer架构,通过海量参数(数十亿至万亿级)和超大规模训练…

阅读更多 →
基于 kNN 边距的可审计嵌入证据信号:agent-governance-toolkit 默认关闭、仅证据型 Prompt Injection 检测模块全解析 2026/9/19 8:20:42

基于 kNN 边距的可审计嵌入证据信号:agent-governance-toolkit 默认关闭、仅证据型 Prompt Injection 检测模块全解析

基于 kNN 边距的可审计嵌入证据信号:agent-governance-toolkit 默认关闭、仅证据型 Prompt Injection 检测模块全解析 【免费下载链接】agent-governance-toolkit AI Agent Governance Toolkit — Policy enforcement, zero-trust identity, execution sandboxing, …

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞