题目描述
判断链表中是否存在环(某个节点的 next 指回了前面的节点)。
思路解析
你可以边走边把节点存进集合,如果遇到一个已经存过的,就有环——但要 O(n) 空间。更妙的是快慢指针(Floyd 判圈),只用 O(1) 空间。
两个指针都从头出发,fast 每次 2 步、slow 每次 1 步。没有环,fast 会先冲到 null(返回 false);有环,fast 进环后会一圈圈逼近 slow,每步缩短 1 的距离,最终在环里相遇。
起点 · slow=fast=头:slow、fast 都从头 3 出发。注意末尾 −4 的箭头是橙色回边「↺ 回到 2」——这就是环。
第 1 步 · slow→2, fast→0:slow 走 1 步到 2,fast 走 2 步到 0。两者都已进入环。
第 2 步 · fast 沿回边绕回 2:slow 到 0。fast 走两步:0 → −4 → 沿回边绕回 2,所以 fast 出现在 2 的位置(看着像“后退”,其实是绕了环一圈的一部分)。
第 3 步 · 相遇于 −4!:slow 到 −4;fast 走两步 2 → 0 → −4 也到 −4。两个指针相遇了!这证明链表有环,返回 true。
作为对比:如果链表没有环,fast 一路领先,会先走到链表末尾的 null。循环条件 while fast and fast.next 一旦不成立,就说明无环,返回 false。
Floyd 判圈(龟兔赛跑):进环后 fast 每步把和 slow 的距离缩短 1,绝不会“跳过”,所以一定相遇。找环的入口(142)、找重复数(287)都用它。
参考代码(Python)
1slow = fast = head
2while fast and fast.next: # 无环时这里会停
3 slow = slow.next # 慢走 1 步
4 fast = fast.next.next # 快走 2 步
5 if slow is fast: return True # 相遇 = 有环
6return False # fast 到 null = 无环复杂度分析
- 时间复杂度:O(n) —— 有环时 fast 最多绕一圈追上
- 空间复杂度:O(1) —— 两个指针,不用哈希
套路模板
记住骨架:快慢同出发、快2慢1、相遇即有环、fast到null即无环。要找环入口,就在相遇后再用一个指针从头与 slow 同速走,相遇点就是入口。
1slow = fast = head
2while fast and fast.next:
3 slow, fast = slow.next, fast.next.next
4 if slow is fast: return True # 相遇=有环
5return False易错点
- ❌ 用 slow.val == fast.val 判相遇 → ✅ 用 slow is fast 判同一节点(不同节点可能值相同,要比身份不比值)
- ❌ 循环条件漏判 fast.next → ✅ while fast and fast.next(fast 跨两步前要确保中间节点存在)
以上文字与上方动画完全同一思路:先看动画建立直觉 → 读文字巩固 → 关掉页面自己默写一遍代码,能写出来才算真的掌握。