文章目录
一、题目描述
给定一个链表,返回链表 开始入环的第一个节点 。
如果链表中没有环,则返回 null。
注意:不允许修改链表结构。
二、示意图与问题本质
假设链表结构如下:

在这里,链表的尾节点 -4 指向了 2 节点,从而形成了一个环。题目要求返回环开始的节点,即节点值为 2 的节点。
三、思维导图
环形链表 II
定义与目标
查找入环节点
无环返回 null
解法路线
快慢指针(Floyd 判圈)
哈希集合法
分析指标
时间复杂度
空间复杂度
代码实现
四、解法一:哈希集合法(直观易懂)
思路
我们可以用一个 Set 记录访问过的节点。
每次遍历时,检查当前节点是否已存在于集合中:
- 若存在,说明当前节点即为 入环节点。
- 若不存在,则继续向下走并添加到集合中。
流程图
是
否
是
否
当前节点 head
节点是否为空?
返回 null
Set 中是否包含该节点?
返回当前节点
将当前节点加入 Set
head = head.next
时间与空间复杂度
- 时间复杂度:O(n)
- 空间复杂度:O(n)
Java 代码
java
public class Solution {
public ListNode detectCycle(ListNode head) {
Set<ListNode> visited = new HashSet<>();
ListNode curr = head;
while (curr != null) {
if (visited.contains(curr)) {
return curr; // 找到入环点
}
visited.add(curr);
curr = curr.next;
}
return null; // 无环
}
}
优缺点
| 优点 | 缺点 |
|---|---|
| 思路直观,容易实现 | 需要额外空间存储访问节点 |
五、解法二:快慢指针法(Floyd 判圈算法)
思路
使用两个指针:
- 快指针
fast每次走两步。 - 慢指针
slow每次走一步。
- 如果存在环,
fast与slow最终会在环中相遇。 - 相遇后,将
fast指针置回head,两个指针每次都走一步。 - 当它们再次相遇时,相遇点即为 入环节点。
原理推导
设:
- 链表从头到入环点的长度为
a - 入环点到第一次相遇点的长度为
b - 环的长度为
r
第一次相遇时:
slow 走了 a + b
fast 走了 a + b + k*r (k为圈数)
因为 fast 的速度是 slow 的两倍:
2(a + b) = a + b + k*r
=> a = k*r - b
这意味着:
从起点走 a 步,与从相遇点走 r - b 步(即再次绕环)会在入环节点相遇。
时序图
快指针 慢指针 链表头 快指针 慢指针 链表头 loop [移动指针直到相遇或结束] loop [同步前进直到再次相遇] 初始化 初始化 前进 1 步 前进 2 步 相遇于环中 重置至链表头 前进一步 前进一步 再次相遇 ->> 入环节点
时间与空间复杂度
- 时间复杂度:O(n)
- 空间复杂度:O(1)
Java 代码
java
public class Solution {
public ListNode detectCycle(ListNode head) {
if (head == null) return null;
ListNode slow = head, fast = head;
// 第一次相遇
while (fast != null && fast.next != null) {
slow = slow.next;
fast = fast.next.next;
if (slow == fast) {
// 第二阶段
fast = head;
while (fast != slow) {
fast = fast.next;
slow = slow.next;
}
return fast; // 入环节点
}
}
return null; // 无环
}
}
优缺点
| 优点 | 缺点 |
|---|---|
| 不需要额外空间 | 理解原理相对抽象,需要数学推导 |
六、总结
| 解法 | 思路 | 时间复杂度 | 空间复杂度 | 适用场景 |
|---|---|---|---|---|
| 哈希集合法 | 记录访问过的节点 | O(n) | O(n) | 快速验证思路 |
| 快慢指针法 | 数学+双指针 | O(n) | O(1) | 实际应用最优方案 |