当前位置: 首页 > article >正文

【码道初阶】Floyd判圈法个人总结:快慢双指针判断环形列表(适用Leetcode142)


Floyd判圈算法详解:如何检测链表中的环并找到环的起点

一、算法概述

Floyd判圈算法(又称龟兔赛跑算法)是解决链表环路检测问题的经典方法。它通过快慢指针(一个快指针每次走两步,一个慢指针每次走一步)来判断链表是否存在环,并在存在环时找到环的起始点。

核心思想

  1. 检测环的存在:快慢指针最终会在环内相遇。
  2. 找到环的起点:相遇后,重置一个指针到链表头,两个指针以相同速度前进,再次相遇的点即为环的入口。

二、代码逐行解析

ListNode *detectCycle(ListNode *head) {
    ListNode *slow = head, *fast = head;
    bool is_first_cycle = true;

    // 第一阶段:检测链表中是否存在环
    while (fast != slow || is_first_cycle) {
        // 如果快指针到达链表尾部,说明无环
        if (fast == nullptr || fast->next == nullptr) {
            return nullptr;
        }
        fast = fast->next->next; // 快指针每次走两步
        slow = slow->next;       // 慢指针每次走一步
        is_first_cycle = false;  // 标记已离开初始状态
    }

    // 第二阶段:找到环的入口
    fast = head; // 重置快指针到链表头
    while (fast != slow) {
        fast = fast->next; // 快慢指针每次各走一步
        slow = slow->next;
    }
    return fast; // 返回环的入口
}

三、详细步骤与原理

1. 第一阶段:检测环的存在

初始化指针
  • slowfast初始都指向头节点head
  • is_first_cycle标记为true,确保第一次循环能进入。
循环移动快慢指针
  • 快指针移动fast = fast->next->next(每次两步)。
  • 慢指针移动slow = slow->next(每次一步)。
  • 终止条件
    • fastfast->nextnullptr,说明链表无环。
    • 若快慢指针相遇(fast == slow),说明存在环。
为什么能检测到环?
  • 数学原理:假设环存在,快指针每次比慢指针多走一步,最终会追上慢指针。
  • 时间复杂度:O(n),其中n为链表长度。

2. 第二阶段:找到环的起点

重置快指针
  • fast重新指向头节点headslow保持在相遇点。
同步移动快慢指针
  • 快慢指针每次各走一步,直到再次相遇。
  • 相遇点即为环的入口。
数学证明
  • 设链表头到环入口距离为a,环入口到相遇点距离为b,环长度为L = b + c(c为相遇点到环入口的距离)。
  • 当快慢指针第一次相遇时:
    • 慢指针走了a + b步。
    • 快指针走了a + b + kL步(k为绕环次数)。
    • 由于快指针速度是慢指针的两倍,有:
      (2(a + b) = a + b + kL)
      化简得:
      (a = (k-1)L + c)
  • 重置快指针后,快指针走a步到达环入口,慢指针走c + (k-1)L步也到达入口。

四、边界情况与示例

1. 链表无环

  • 示例1 -> 2 -> 3 -> nullptr
  • 执行过程
    • 快指针很快遇到nullptr,返回nullptr

2. 链表有环,头节点是入口

  • 示例1 -> 2 -> 3 -> 1
  • 执行过程
    • 快慢指针在节点1相遇。
    • 重置快指针到头部,两者同步移动一步后相遇于节点1。

3. 链表有环,环较深

  • 示例1 -> 2 -> 3 -> 4 -> 5 -> 2
  • 执行过程
    • 快慢指针在环内某处相遇。
    • 重置快指针后,两者同步移动至节点2(入口)。

五、算法复杂度

  • 时间复杂度:O(n)
    • 检测环最多需要遍历链表两次。
  • 空间复杂度:O(1)
    • 仅使用两个指针,无额外空间。

六、常见问题解答

1. 为什么快指针要走两步?

  • 保证相遇:快指针每次比慢指针多走一步,确保在环内必然追上。

2. 如何处理空链表?

  • 初始检查中,若headnullptr,直接返回nullptr

3. 为什么第二阶段能正确找到入口?

  • 数学推导:通过公式证明,同步移动的指针最终会在入口相遇。


http://www.kler.cn/a/535452.html

相关文章:

  • 哈希(Hashing)在 C++ STL 中的应用
  • 基础篇05-直方图操作
  • 凝思60重置密码
  • 禅道社区版项目管理软件部署(记录篇)
  • Ubutun本地部署DeepSeek R1
  • 保姆级教程Docker部署KRaft模式的Kafka官方镜像
  • Java全栈项目-校园闲置教室查询系统
  • 【漫画机器学习】082.岭回归(或脊回归)中的α值(alpha in ridge regression)
  • 机器学习模型--线性回归、逻辑回归、分类
  • 【vue3 入门到实战】7. 标签中的 ref
  • 代码随想录day06
  • VScode如何使用deepseek详细教程
  • 某音小程序反编译签名加密静态分析
  • Mac下使用brew安装go 以及遇到的问题
  • 面试笔记-多线程篇
  • uv 安装包
  • hadoop生态 apache-Flume-1.8.0 的安装和 使用
  • 【Linux】如何创建一个可定时删除的文件
  • Kali Linux 渗透测试环境配置(Metasploit + Burp Suite)
  • 源路由 | 源路由网桥 / 生成树网桥
  • 面试笔记-基础篇
  • 网络安全辅助系统 框架图 网络安全模块
  • Vue中的的通信方式有几种?
  • vue2+vue3 HMCXY基础入门
  • ONE NET MQTT+HTTP多端控制
  • 实际时钟(RTC)的介绍