#141
简单
高频
双指针环形链表
这是一道围绕链表展开的高频练习。建议先掌握「双指针」这套写法,再结合下方步骤讲解理解状态维护、边界处理和复杂度取舍。
链表
题目分析
给你一条链表,要求判断它是否存在环。
如果某个节点沿着 next 指针继续走,最终又能回到之前访问过的节点,就说明存在环。
一句话概括:
让两个速度不同的指针同时前进,如果能相遇,就说明链表里有环。
接下来怎么看推荐代码: 带着这个理解再看推荐代码时,重点观察这条主线:让慢指针一次走一步、快指针一次走两步;如果链表有环,它们最终一定会在环内相遇。
推荐代码
推荐解法:双指针
时间复杂度: O(n)
空间复杂度: O(1)
核心思路: 让慢指针一次走一步、快指针一次走两步;如果链表有环,它们最终一定会在环内相遇。
public class Solution {
public boolean hasCycle(ListNode head) {
if (head == null || head.next == null) {
return false;
}
ListNode slow = head;
ListNode fast = head.next;
while (slow != fast) {
if (fast == null || fast.next == null) {
return false;
}
slow = slow.next;
fast = fast.next.next;
}
return true;
}
}
结构化讲解
面试时怎么讲
开场思路
这题我会用 Floyd 快慢指针判环,不需要额外空间。
核心过程
- 让慢指针每次走一步,快指针每次走两步。
- 如果链表无环,快指针会先走到空指针,循环结束。
- 如果链表有环,快指针进入环后会不断追赶慢指针。
- 两者只要相遇,就说明链表中存在环。
复杂度总结
时间复杂度 O(n),空间复杂度 O(1)。
面试补一句:这题真正依赖的是追及关系,不是链表的值。
核心思路
判断链表有环不需要额外哈希表。只要快慢指针进入了同一个环,快指针相对慢指针每轮都会缩短距离,所以迟早相遇。
步骤讲解
1
初始化快慢指针
让 slow、fast 都从头节点出发,分别代表慢速和快速遍历者。
为什么这样做:后续是否相遇,取决于它们在同一条链上的相对速度差。
对应代码提示:ListNode slow = head, fast = head;
2
快走两步,慢走一步
循环中让慢指针前进一步,快指针前进两步。
为什么这样做:如果存在环,快指针会在环内不断追上慢指针。
对应代码提示:slow = slow.next; fast = fast.next.next;
3
相遇则说明有环
一旦两个指针指向同一个节点,就可以直接返回 true。
为什么这样做:在无环链表里,快指针只会先到空指针,不可能回头相遇。
对应代码提示:if (slow == fast) return true;
易错点
没先判断快指针是否还能走两步
无环链表结尾会出现空指针,直接取 fast.next.next 会报错。
正确理解:循环条件写成
fast != null && fast.next != null。认为相遇只能发生在入环点
实际相遇点可能是环上的任意位置。
正确理解:这里只需要判断“是否相遇”,不需要限定具体节点。
把快慢指针都写成一步
速度差不存在,就无法利用追及关系判断有环。
正确理解:必须保持快指针两步、慢指针一步。
复杂度与适用判断
时间复杂度:O(n)
空间复杂度:O(1)
比其他方案更好在哪里:比哈希表判重更省空间,也更符合链表题常见面试要求。
适用判断:链表中只要涉及环、相遇、距离差,优先考虑快慢指针。
额外提醒
- 无环时快指针出界,有环时快慢指针相遇。
动画演示
如果你还没完全看懂,可以展开动画演示。
双指针动画演示
想看这段解法是怎么一步步运行的,可以展开动画继续观察。
双指针动画演示
想看这段解法是怎么一步步运行的,可以展开动画继续观察。
准备就绪 - 输入链表值和环的位置
动画演示仅在桌面端提供,移动端请优先阅读推荐代码与结构化讲解。