LeetCode 141.环形链表(快慢指针|链表)

0 阅读3分钟

image.png

image.png

一、核心思路

判断单链表是否存在环。链表有环:某个节点的 next 指针可以再次访问到之前出现过的节点;有环返回 true,无环返回 false。 采用弗洛伊德龟兔赛跑算法(快慢指针)

  1. 慢指针slow每次走 1 步,快指针fast每次走 2 步,两个指针都从 head 出发。
  2. 如果链表不存在环:快指针会先走到链表末尾null,退出循环,返回 false。
  3. 如果链表存在环:快指针进入环后会一直在环内循环,速度更快,最终一定会追上慢指针。当slow == fast节点引用相等,内存地址相同)代表相遇,证明存在环,返回 true。

循环条件:fast != null && fast.next != null 目的:保证执行fast.next.next不会触发空指针异常。

二、代码实现

/**
 * Definition for singly‑linked list.
 * class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public boolean hasCycle(ListNode head) {
        ListNode slow = head;
        ListNode fast = head;
        while(fast != null && fast.next != null){
            slow = slow.next;
            fast = fast.next.next;
            // 判断节点引用是否为同一个对象
            if(slow == fast){
                return true;
            }
        }
        // fast走到null,链表无环
        return false;
    }
}

时间复杂度:(O(n)) 空间复杂度:(O(1)),仅使用两个指针变量

备选思路 HashSet:遍历把节点存入集合,检测重复节点,空间复杂度 O (n),不推荐面试使用。

三、实例演示

样例 1:有环链表 3 →2 →0 →‑4‑4.next = 2

初始化:slow=3,fast=3,循环条件fast != null && fast.next != null

轮次slowfastslow = slow.nextfast = fast.next.nextslow == fast
初始33--
第 1 轮33slow 走到 2fast 走到 0
第 2 轮20slow 走到 0fast 走到 2
第 3 轮02slow 走到‑4fast 走到‑4✅相遇,return true

样例 2:无环链表 1 →2 →3 →null

初始化:slow=1,fast=1

轮次slowfastslow = slow.nextfast = fast.next.nextslow == fast
初始11--
第 1 轮11slow 走到 2fast 走到 3
第 2 轮23slow 走到 3fast.next == null,循环终止-

循环退出,返回 false。

四、语法坑、边界问题

  1. slow == fast比较的是节点对象引用(内存地址),不能比较 val 值。不同节点 val 相同,但不是同一个节点,不能判定有环。

  2. while 循环条件 fast != null && fast.next != null,两个条件缺一不可;如果缺少fast.next != null,执行fast.next.next报空指针。

  3. slow 与 fast 初始值都赋值head,不要初始化为head.next,会错过初始就相遇的场景。

  4. 边界用例:

    • head == null空链表:直接跳出循环返回 false,代码兼容。
    • 单个节点,节点 next 指向自己(自环):快慢指针可以正常相遇返回 true。
  5. 有环链表永远不会出现fast = null,指针一直在环中循环。

五、内置函数 / 语法总结

  1. fast = fast.next.next:链表一次性向后跳两步,使用前必须保证 fast.next 不为 null
  2. 链表引用相等==:判断是否是同一个节点对象;不能拿==判断节点内部数值。
  3. while 链表遍历终止:无环链表靠 fast 碰到 null 结束循环。

六、个人感悟

  1. 快慢指针两大用途:①找链表中点(234 回文链表);②检测链表环(141 环形链表);两套代码循环条件完全一样。
  2. 核心逻辑:有环,快指针一定能追上慢指针;无环,快指针先抵达链表末尾。
  3. 注意区分:==对比地址,.val对比数值,链表题高频踩坑点。
  4. 拓展:142 环形链表 II,在本题基础上进一步寻找环的入口节点。