LeetCode 142:环形链表 II —— 快慢指针定位环的起点(JavaScript)

🌟 题目描述

给定一个链表的头节点 head,返回链表开始入环的第一个节点。如果链表无环,则返回 null

💡 注意:不允许修改链表结构。

示例 1:

makefile 复制代码
输入: head = [3,2,0,-4], pos = 1
输出: 返回索引为 1 的节点(值为 2)
解释: 链表有一个环,尾部连接到第二个节点。

示例 2:

ini 复制代码
输入: head = [1,2], pos = 0
输出: 返回索引为 0 的节点(值为 1)

示例 3:

ini 复制代码
输入: head = [1], pos = -1
输出: null

🔍 问题分析

这是一道经典的 环检测 + 环入口定位 问题。关键点:

  • 如何判断链表是否有环?
  • 如果有环,如何找到环的起始节点

直接遍历会陷入死循环,所以需要更聪明的方法------快慢指针(Floyd 循环查找算法)


💡 核心思路:Floyd 判圈算法

✅ 第一步:用快慢指针判断是否存在环

  • slow 指针每次走一步;
  • fast 指针每次走两步;
  • 若存在环,两者一定会相遇(因为快指针会追上慢指针);
  • 若无环,fast 会先到达 null

✅ 第二步:找到环的入口节点

当快慢指针相遇后,从头节点和相遇点同时出发,每次走一步,再次相遇的点就是环的入口

🧠 数学证明简述

设:

  • 链表头部到环入口距离为 a
  • 环的长度为 b
  • 相遇点距离入口为 c

则:

  • 慢指针走了:a + c
  • 快指针走了:a + c + k*b(k 是整数)
  • 因为快指针速度是慢指针的 2 倍:2(a + c) = a + c + k*b
  • 推出:a = k*b - c
  • 即:从头节点走 a 步,和从相遇点走 a 步,会在入口处相遇!

✅ JavaScript 实现(完整代码)

ini 复制代码
/**
 * Definition for singly-linked list.
 * function ListNode(val, next) {
 *     this.val = (val === undefined ? 0 : val)
 *     this.next = (next === undefined ? null : next)
 * }
 */

/**
 * @param {ListNode} head
 * @return {ListNode}
 */
var detectCycle = function(head) {
    let slow = head;
    let fast = head;

    // 第一步:快慢指针找相遇点
    while (fast && fast.next) {
        slow = slow.next;
        fast = fast.next.next;

        if (slow === fast) {
            // 找到环,进入第二步:找入口
            let x1 = head;  // 从头开始
            let x2 = fast;  // 从相遇点开始

            while (x1 !== x2) {
                x1 = x1.next;
                x2 = x2.next;
            }

            return x1; // 相遇点即为环的入口
        }
    }

    // 无环,返回 null
    return null;
};

🧠 图解演示(以 [3→2→0→-4→2] 为例)

yaml 复制代码
链表:3 → 2 → 0 → -4 → ↲ 2(环)
      ↑             ↑
      |             |
      -----------------

1. 快慢指针移动:
   slow: 3 → 2 → 0 → -4 → 2
   fast: 3 → 0 → 2 → -4 → 2 → 0 → 2 → ...

2. 在节点 2 相遇(快慢指针相等)

3. 重置 x1 = head(3), x2 = 相遇点(2)
   x1: 3 → 2
   x2: 2 → 0 → -4 → 2 → ...
   在节点 2 再次相遇 → 返回该节点

📊 复杂度分析

项目 复杂度
时间复杂度 O(n) ------ 最多遍历两次链表
空间复杂度 O(1) ------ 只使用常数额外空间

优势:无需哈希表,不修改链表,适用于大链表场景。


🛠️ 面试常见问题

❓ "为什么相遇后,从头和相遇点同时走能相遇于入口?"

回答:这是由数学关系 a = k*b - c 决定的,两个指针走的路径总长相同,最终会在入口处相遇。

❓ "能否用哈希表解决?"

可以,但空间复杂度为 O(n),而快慢指针法是 O(1),更优。

❓ "如果链表很长,快指针会不会越界?"

不会,因为 while(fast && fast.next) 已经做了安全判断。

✅ 总结

其实这题就先设置两个指针慢的一步一步走而快的则两步两步走如果存在环则必定相遇,如果相遇就可以通过这幅图推出a=c+(n−1)(b+c)那就差不多能写出来了

相关推荐
梦6502 小时前
什么是react?
前端·react.js·前端框架
CoovallyAIHub2 小时前
OCR战场再起风云:LightOnOCR-1B凭什么比DeepSeekOCR快1.7倍?(附演示开源地址)
深度学习·算法·计算机视觉
zhougl9962 小时前
cookie、session、token、JWT(JSON Web Token)
前端·json
Ryan今天学习了吗2 小时前
💥不说废话,带你上手使用 qiankun 微前端并深入理解原理!
前端·javascript·架构
高端章鱼哥2 小时前
前端新人最怕的“居中问题”,八种CSS实现居中的方法一次搞懂!
前端
冷亿!2 小时前
Html爱心代码动态(可修改内容+带源码)
前端·html
Predestination王瀞潞2 小时前
Java EE开发技术(第六章:EL表达式)
前端·javascript·java-ee
掘金012 小时前
在 Vue 3 项目中使用 MQTT 获取数据
前端·javascript·vue.js
QuantumLeap丶2 小时前
《uni-app跨平台开发完全指南》- 03 - Vue.js基础入门
前端·vue.js·uni-app