141. 环形链表

141. 环形链表

✨核心逻辑

本题采用 快慢指针(龟兔赛跑) 的策略:

  1. 基础边界:如果链表为空或者只有一个节点,肯定没有环,直接返回 false
  2. 双指针初始化:定义慢指针 s 指向头结点,快指针 f 指向头结点的下一个节点。快指针每次走两步,慢指针每次走一步。
  3. 追逐与相遇
    • 如果链表存在环,快指针最终一定会追上慢指针(相当于在环形跑道上,跑得快的人套圈追上了跑得慢的人)。
    • 如果链表没有环,快指针一定会先走到 nullnull.next
  4. 检测循环:在循环中判断快指针是否触及边界;如果快慢指针相遇(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 如果仅有一个,那么你在跳跃两个指针获取快指针时,会报空指针异常