141. 环形链表
✨核心逻辑
本题采用 快慢指针(龟兔赛跑) 的策略:
- 基础边界:如果链表为空或者只有一个节点,肯定没有环,直接返回
false。 - 双指针初始化:定义慢指针
s指向头结点,快指针f指向头结点的下一个节点。快指针每次走两步,慢指针每次走一步。 - 追逐与相遇:
- 如果链表存在环,快指针最终一定会追上慢指针(相当于在环形跑道上,跑得快的人套圈追上了跑得慢的人)。
- 如果链表没有环,快指针一定会先走到
null或null.next。
- 检测循环:在循环中判断快指针是否触及边界;如果快慢指针相遇(
s == f),则说明找到了环,返回true。
🔥代码实现(含详细变量注释)
public class Solution {
public boolean hasCycle(ListNode head) {
// 边界情况判断:如果链表为空,或者链表只有一个节点,肯定不存在环
if (head == null || head.next == null) {
return false;
}
// s:慢指针(Slow),初始指向链表的头节点,每次走一步
ListNode s = head;
// f:快指针(Fast),初始指向头节点的下一个节点,每次走两步
ListNode f = head.next;
// 当快慢指针不相遇时,持续循环追赶
while (s != f) {
// 在快指针走两步之前,先判断它是否即将走到尽头
// 如果 f 为空 或者 f.next 为空,说明链表走到了终点,没有环
if (f == null || f.next == null) {
return false;
}
// 慢指针向前走一步
s = s.next;
// 快指针向前走两步
f = f.next.next;
}
// 如果循环结束且没有返回 false,说明快慢指针相遇了,链表存在环
return true;
}
}
- ⏱️复杂度分析
时间复杂度:O(N),其中 N 是链表的节点数。如果链表无环,快指针会遍历到链表末尾,耗时 O(N);如果链表有环,快指针会在环形部分追上慢指针,圈内步数不超过环长,总步数也不超过 O(N)。
空间复杂度:O(N),其中 N 是链表的节点数。如果链表无环,快指针会遍历到链表末尾,耗时 O(N);如果链表有环,快指针会在环形部分追上慢指针,圈内步数不超过环长,总步数也不超过 O(N)。
🔍总结一下:
while的if条件很关键,必须同时包含 f == null 以及 f.next == null 如果仅有一个,那么你在跳跃两个指针获取快指针时,会报空指针异常