一、题目大意
给定链表头结点head,找出链表环的入口节点;链表无环返回null。
注意:判断的是节点引用 (内存地址),不是节点数值;不允许修改链表。
二、解题思路
方法 1:Floyd 快慢指针(O (1) 空间,面试最优)
分为两个阶段:
- 阶段 1:找相遇点 slow 每次走 1 步,fast 每次走 2 步。 如果存在环,快慢指针一定会在环内某个节点相遇;如果 fast 走到
null,代表无环。
⚠️重点:相遇点≠环入口,不能直接返回相遇节点。
- 阶段 2:寻找环入口 数学推导结论:头结点到环入口距离 = 相遇点继续走到环入口的距离(允许绕完整的环)。
- 将 slow 指针重置到链表头部,fast 停留在相遇点
- slow、fast 两个指针每次都只走 1 步
- 再次相遇的节点,就是环入口。
数学简要推导
- a:头到环入口距离;b:环入口到相遇点;c:环长度
- (slow: a+b);(fast:2(a+b))
- (2(a+b)=a+b+n\cdot c),化简得到 (\boldsymbol{a = n\cdot c-b})
完整 Java 代码
public class Solution {
public ListNode detectCycle(ListNode head) {
ListNode slow = head;
ListNode fast = head;
//阶段1:快慢指针找相遇点
while(fast != null && fast.next != null){
slow = slow.next;
fast = fast.next.next;
//发生相遇,存在环
if(slow == fast){
slow = head; //slow放回表头
//两个指针每次走一步
while(slow != fast){
slow = slow.next;
fast = fast.next;
}
return slow; //再次相遇即为环入口
}
}
//fast走到null,链表无环
return null;
}
}
三、实例演示
样例链表:3 → 2 → 0 → -4,-4.next =2,环入口节点为 2
阶段 1 找相遇点
| 轮次 | slow | fast |
|---|---|---|
| 初始 | 3 | 3 |
| 第 1 轮 | 2 | 0 |
| 第 2 轮 | 0 | 2 |
| 第 3 轮 | -4 | -4 |
✅相遇点:-4(不是环入口)
阶段 2 同步一步一步走
| 轮次 | slow (重置 head=3) | fast (停留在 - 4) | 判断 |
|---|---|---|---|
| 初始 | 3 | -4 | 不相等 |
| 走一步 | 2 | 2 | ✅相等,返回节点 2 |
四、方法 2:HashSet 哈希表(空间 O (n),逻辑简单)
核心思想
遍历链表,把节点对象(引用)存入集合。 第一次遇到已经存在集合中的节点,就是环入口;遍历到 null 代表无环。
⚠️注意:存的是节点,不是节点 val;val 重复不等于链表有环。
public class Solution {
public ListNode detectCycle(ListNode head) {
Set<ListNode> visited = new HashSet<>();
ListNode cur = head;
while(cur != null){
if(visited.contains(cur)){
return cur;
}
visited.add(cur);
cur = cur.next;
}
return null;
}
}
实例演示: cur 依次遍历3、2、0、-4加入集合;当 cur 再次走到2,集合中已经存在节点 2,直接返回节点 2。
五、两种方法对比
| 方案 | 时间复杂度 | 空间复杂度 | 特点 |
|---|---|---|---|
| Floyd 快慢指针 | O(n) | O(1) | 原地算法,面试首选;需要理解数学推导 |
| HashSet 哈希表 | O(n) | O(n) | 逻辑简单,不容易写错;消耗额外内存 |
六、语法坑与边界总结
-
while 循环条件顺序:
fast != null && fast.next != null,顺序不能颠倒,否则会空指针异常。 -
第一次快慢指针相遇 ≠ 环入口,千万不能直接 return。
-
第二阶段两个指针全部只能走一步,fast 不要再执行
next.next。 -
特殊边界:
- 整个链表自环(head 就是环入口),相遇后直接返回 head;
- 无环链表,fast 走到 null 返回 null;
-
HashSet 比较的是节点内存地址,不是 val,避免数值相同造成误判。
七、个人感悟
141 题只需要判断是否存在环;142 题需要找到环入口,多了第二阶段同步遍历。快慢指针的结论需要记住:相遇后,一个指针放回表头,两者同步前进,再次相遇点就是环入口。哈希表写题很快,但面试优先考察 O (1) 空间的 Floyd 算法。