 时间 O(1) 空间))
示例工程教程【免费下载链接】leetcodeLeetCode solutions in any programming language | 多种编程语言实现 LeetCode、《剑指 Offer第 2 版》、《程序员面试金典第 6 版》题解项目地址https://gitcode.com/doocs/leetcode点击查看免费下载导读本文以 doocs/leetcode 仓库中 lcci/02.06.Palindrome Linked List/README.md 为主体深度剖析《程序员面试金典第 6 版》面试题 02.06「回文链表」的完整解法。你将掌握如何用快慢指针在 O(n) 时间内定位链表中点、如何原地反转后半段链表并在 O(1) 额外空间内完成回文判定同时对照仓库中 Python、Java、C、Go、TypeScript、JavaScript、C#、Swift 八种语言的 Solution 实现理解同一算法在不同语言下的写法差异。题目描述编写一个函数检查输入的链表是否是回文的。示例 1输入1-2 输出false示例 2输入1-2-2-1 输出true进阶你能否用 O(n) 时间复杂度和 O(1) 空间复杂度解决此题所谓回文链表即正序读与逆序读结果一致的链表如1-2-2-1、1-2-3-2-1。题目要求编写函数判断输入的链表是否回文难度标记为「简单」但进阶条件对空间复杂度提出了明确约束这决定了朴素的「拷贝数组」方案并非本题的最优答案。解法总览为什么不能直接拷贝数组最容易想到的思路是把链表所有节点的值拷贝进一个数组再用双指针从两端向中间比较。以仓库文档中的思考批注为参考判断链表是否回文可将值拷入数组再双指针比较空间 O(n)。题目允许改写链表时希望额外空间为常数。也就是说数组法的时间复杂度是 O(n)但空间复杂度同样是 O(n)不满足进阶要求。当题目允许改写链表结构时我们完全可以在链表本身上做文章回文比较的本质是「前半段」与「反转后的后半段」逐节点相等。于是算法被拆解为三个步骤用快慢指针找到链表的中点将中点之后的链表原地反转同步遍历前半段与反转后的后半段逐节点比较。整个过程只使用若干个指针变量额外空间为 O(1)时间复杂度为 O(n)正好满足进阶约束。步骤一快慢指针定位中点算法先处理边界如果链表为空直接返回true空链表视为回文。随后令slow、fast两个指针同时从head出发其中slow每次走一步fast每次走两步。仓库文档对中点位置给出了精确描述如果链表长度为奇数那么慢指针指向的就是中点如果链表长度为偶数那么慢指针指向的是中间两个节点的前一个节点。值得注意的细节是在仓库的解法实现中fast的初始值是head.next而非head。以 Python 实现为例Solution.pyslow, fast head, head.next while fast and fast.next: slow slow.next fast fast.next.next循环终止后奇数长度链表如1-2-3-2-1长度 5slow落在正中间节点3上slow.next即后半段起点偶数长度链表如1-2-2-1长度 4slow落在左侧中间节点第一个2上slow.next同样指向后半段起点。这种「从head.next出发的快指针」初始化方式使得两种长度下slow.next都恰好是后半段的头节点无需再单独判断奇偶代码更统一。步骤二原地反转后半段链表定位到中点后p slow.next指向后半段头节点。此时必须执行关键的一步——断开连接p slow.next slow.next None文档思考批注说明了原因偶数长度时慢指针停在左中点从其next起反转比较时只走后半长度。断开slow.next以免与反转链交织。若不断开slow.next反转过程中很容易与原链表前段形成环或相互纠缠导致遍历无法终止或比较错乱断开后前半段与后半段成为两条互不相交的链。接下来用「哑节点 头插法」原地反转后半段。仓库中所有语言实现都采用了同一个模板以 Python 为例dummy ListNode() while p: next p.next p.next dummy.next dummy.next p p next p dummy.next逐行拆解这个反转循环next p.next先保存当前节点的后继防止断链后丢失p.next dummy.next把当前节点指向已反转链的头部即dummy.next实现头插dummy.next p更新已反转链的头部为当前节点p next移动到下一个待处理节点。循环结束后dummy.next就是反转后后半段的新头节点。该手法在 JavaSolution.java、CSolution.cpp、GoSolution.go等语言中结构完全一致只是语法不同——例如 C 中为ListNode* dummy new ListNode(0)Go 中为dummy : ListNode{}Swift 中为var dummy ListNode(0)。步骤三前半段与反转后半段逐节点比较反转完成后让head继续指向原链表头前半段起点p指向反转后的后半段头节点同步遍历并比较while p: if head.val ! p.val: return False head head.next p p.next return True这里只以p后半段是否遍历完作为循环条件因为前半段长度总是大于等于后半段奇数长度时前半段多出正中间的一个节点但它不需要与任何节点配对。一旦发现某对节点值不相等立即返回false全部比较完毕则返回true。需要留意 Swift 实现中的一个细节Solution.swift由于 Swift 的head是可选类型ListNode?比较阶段使用了局部变量currentHead承接头节点避免在遍历过程中修改原参数的可选性语义同时用currentHead?.val ! p?.val进行安全取值比较——这正是把同一算法移植到强类型、可选值语言时需要注意的适配点。复杂度分析时间复杂度 O(n)快慢指针遍历一次链表定位中点O(n)反转后半段O(n/2)同步比较O(n/2)三者相加仍为 O(n)其中 n 为链表长度空间复杂度 O(1)全程只使用了slow、fast、p、dummy、next等固定数量的指针变量没有借助任何与链表规模相关的额外存储。仓库文档明确给出结论「时间复杂度 O(n)其中 n 为链表的长度。空间复杂度 O(1)。」这恰好满足题目进阶要求。边界情况梳理输入预期结果处理方式空链表head nulltrue开头直接返回见各语言if (!head) return true分支单节点链表true快慢指针循环不进入slow.next为null比较循环不执行直接返回true偶数长度回文1-2-2-1trueslow落在左中点反转2-1为1-2与1-2比较奇数长度回文1-2-3-2-1trueslow落在中点3反转2-1前半段多出的3无需比较非回文1-2false比较到第一个不相等节点即返回多语言实现对照仓库为本题提供了 8 种语言的完整实现全部位于 lcci/02.06.Palindrome Linked List 目录下算法骨架完全一致语言源文件关键语法差异Python3Solution.pydummy ListNode()可无参构造动态类型无需显式判空指针JavaSolution.javanew ListNode(0)构造哑节点! null显式判空CSolution.cppnew ListNode(0)堆上分配nullptr判空GoSolution.goListNode{}取地址构造nil判空多返回值赋值slow, fast slow.Next, fast.Next.NextTypeScriptSolution.ts类型标注ListNode | null!严格不等比较JavaScriptSolution.js无类型标注!严格不等比较C#Solution.cs公有类public class SolutionIsPalindrome遵循 PascalCase 命名SwiftSolution.swift可选链?.比较阶段引入currentHead局部变量适配可选类型例如 TypeScript 实现Solution.ts完整代码如下可用于直接提交function isPalindrome(head: ListNode | null): boolean { if (!head) { return true; } let slow head; let fast head.next; while (fast fast.next) { slow slow.next; fast fast.next.next; } let p slow.next; slow.next null; const dummy new ListNode(0); while (p) { const next p.next; p.next dummy.next; dummy.next p; p next; } p dummy.next; while (p) { if (head.val ! p.val) { return false; } head head.next; p p.next; } return true; }题目在仓库中的定位与延伸本题属于 doocs/leetcode 仓库的lcci分类目录对应《程序员面试金典第 6 版》系列题解。除本题外该目录还收录了如 02.01.Remove Duplicate Node移除重复节点、02.04.Partition List链表分区、02.07.Intersection of Two Linked Lists链表相交等链表类经典面试题同目录下每道题均遵循「README 题解 多语言 Solution」的组织规范lcci.json 记录了该分类的题目标题与难度元数据。本文所讲的「快慢指针定位中点 原地反转」组合拳在同类问题中具有很强的迁移性例如 LeetCode 原题 234. 回文链表 的解法思路与本题完全同源而定位中点、反转链表的技巧也常见于「重排链表」「链表排序」等题目。如果你正在系统备战链表类面试题可顺着本仓库的lcci与solution目录按专题刷题并在每题目录下对照多种语言的实现加深对同一算法思想在不同语言落地方式的理解。小结面试题 02.06「回文链表」的核心考点有三一是能否想到用 O(1) 空间而非拷贝数组二是快慢指针在奇偶长度下的中点定位细节三是原地反转链表时断开slow.next的必要性以及哑节点头插法的写法。掌握了这三步你不仅解决了这一道题也夯实了链表类问题的三个基本功可直接迁移到多道高频面试题中。赞分享示例工程教程【免费下载链接】leetcodeLeetCode solutions in any programming language | 多种编程语言实现 LeetCode、《剑指 Offer第 2 版》、《程序员面试金典第 6 版》题解项目地址https://gitcode.com/doocs/leetcode点击查看免费下载相关推荐doocs/leetcode 题解程序员面试金典 02.06 回文链表——快慢指针与反转链表的 O(1) 空间解法全解析doocs/leetcode 题解程序员面试金典 02.06 回文链表——快慢指针与反转链表的 O 1 空间解法全解析 本篇技术指南围绕 《程序员面试金典第示例工程教程LeetCode-Go 题解143. Reorder List重排链表快慢指针 链表反转 O(1) 空间LeetCode Go 题解143. Reorder List重排链表快慢指针 链表反转 O 1 空间 导读 LeetCode 第 143 题「Re示例工程doocs/leetcode 题解精讲面试题 02.07 链表相交——双指针法的 O(1) 内存实现doocs/leetcode 题解精讲面试题 02.07 链表相交——双指针法的 O 1 内存实现 导读 链表相交Intersection of Two L示例工程教程上一篇Iris 光影模组完整上手指南从安装到调优的 7 个关键步骤下一篇RedwoodJS 本地 Postgres 数据库搭建完全指南从安装、连接配置到 SQLite 迁移创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考