刷链表题的时候,环形链表II(LeetCode 142)是一道大概率绕不过去的坎。很多朋友在141题"判断链表是否有环"上花十分钟就写完了,结果面试官追加一句"那请你找到环的入口节点",当场就卡住了。这道题刷完之后,你会发现快慢指针不只是"追及问题"这么简单,它背后藏着一个非常干净的数学等式,理解了这个等式,你才算真正拿下了环形链表这一整类问题。
这篇文章我打算从题目本身出发,把快慢指针法的推导过程掰开揉碎讲清楚,再给出C++和Python两个完整实现,最后聊一聊环形结构在真实工程里到底从哪儿来,以及怎么用快慢指针的思想去排查线上死循环之类的问题。不管你是正在准备算法面试,还是工作中要和链表、循环结构打交道,这篇都能给你点实在的东西。
1. 环形链表II到底比"判断有环"难在哪里
1.1 题目描述和两种输出要求
先明确一下题目本身。给你一个单链表的头节点head,需要你判断这个链表是否存在环,如果存在,返回环的第一个节点(也就是入环点);如果不存在,返回null。注意,这里不是简单地返回true/false,而是要精确定位到环的入口节点。
这和141题的区别看起来只是"多返回一个节点",但难度差的不是一点半点。判断有没有环,你只要让快慢指针跑起来,一旦相遇就说明有环,跑完了都没碰上就说明没环,整个过程不需要知道任何位置信息。而环形链表II要求你从相遇的信息里再反推出一个具体位置,这就逼着你必须理解两个指针的路程关系,而不是靠背模板。
题目里还有一个隐含约束:不要修改链表结构。这意味着你不能用"破坏链表"的方式来标记节点,比如遍历过程中把每个访问过的节点next指向某个特殊节点,这种思路在思路上可行,但不符合题目要求,而且面试官看到你打算改链表结构,大概率会直接打断你。
1.2 没学快慢指针之前,你能想到哪些方案
先说断链法。思路很简单:从头遍历,每经过一个节点,就把它的next指针指向前一个节点或者某个标记节点。如果遍历过程中发现某个节点的next已经指向了标记节点,说明它之前被访问过,也就是回到了环上。这个办法能定位入环点,但代价是链表被破坏了,原结构无法恢复,在工程里这种操作基本不可接受,在题库里也过不了。
再说哈希标记法。用一个哈希表记录访问过的节点,遍历链表,每次遇到一个新节点就检查它是否已经出现在哈希表里,如果是,那这个节点就是环的入口。这个办法不破坏链表,思路也直观,空间复杂度是O(n),在面试里作为保底方案是合格的,但如果说"最优解",面试官想听的还是快慢指针。
还有一种是"时间戳法",给每个节点结构体里加一个访问标记字段,本质上和哈希法一样,只是空间的载体不同,而且往往需要改动节点定义,更麻烦。
我当初第一次做这道题的时候,第一反应也是哈希表,毕竟"看到重复就返回"这个直觉太自然了。但后来真正理解了快慢指针的推导,才发现一个有意思的现象:哈希法其实是在用"空间记忆"来代替"数学推理",而快慢指针是用速度差来构造一个可计算的等式。后者只需要O(1)空间,而且在推导过程中你会对链表结构有更深的理解,这是哈希法给不了的。
1.3 为什么面试官默认"你应该会快慢指针"
说句实在话,如果你去面试,面试官问环形链表II,他心里默认的解法就是快慢指针,因为这是教科书级别的经典方案,也是考察"你懂不懂数学证明"的一个好切口。
你可能会说:"哈希表也能做啊,为什么非要快慢指针?"这里有个真实的面试逻辑:哈希表方案考察的是你会不会用基础数据结构,快慢指针方案考察的是你能不能从已知现象(相遇)推导未知信息(入口位置)。后者显然更能区分候选人的算法功底,这也是为什么几乎所有题解都会重点讲快慢指针。
但我要多说一句:如果你在面试现场实在想不起来快慢指针怎么证明,先把哈希表方案写出来,保证能跑通、能讲清楚,然后再补一句"我知道还有空间O(1)的快慢指针做法,给我一点时间我可以推出来"。大多数面试官会给你这个时间,因为这不是在考背诵,是在考你有没有逻辑推导能力。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 快慢指针法:两次"相遇"背后的距离恒等式
2.1 规则先摆清楚
快慢指针的规则很简单:慢指针slow每次走1步,快指针fast每次走2步,两个指针都从head出发。如果链表里存在环,那么fast一定会"追上"slow,也就是在某一个节点上两个指针相等。如果链表没有环,fast会先走到链表末尾的空指针,循环结束,返回null。
这里有一个需要想明白的点:fast为什么一定会追上slow?如果链表有环,当slow进入环之后,fast已经在环里绕圈了,由于fast的速度是slow的两倍,两者的相对速度是1步/次,等价于slow不动、fast以每轮1步的速度靠近它,所以只要跑道足够长(环的长度有限),fast必然会在某一点经过slow的位置。
很多人在这一步会犯一个直觉错误:觉得fast速度太快,可能会"跳过"slow。但因为在环上每经过一轮,fast和
