Evan Wang
Evan WangFrontEnd Developer
  • Evan Wang
    Evan WangFrontEnd Developer
  • 前端相关
  • 算法题
  • 日常笔记
  • Chat With AI

两数之和
两数相加
无重复字符的最长子串
最长回文子串
盛水最多的容器
三数之和
电话号码的字母组合
有效括号
合并两个有序链表
括号生成
下一个排列
搜索旋转排序数组
在排序数组中查找元素的第一个和最后一个位置
删除链表的倒数第n个节点
组合总和
全排列
旋转图像
字母异位词分组
最大子数组合
跳跃游戏
反转链表
反转链表2
合并区间
最小路径和
编辑距离
颜色分类
爬楼梯
组合
子集
不同的二叉搜索树
验证二叉搜索树
对称二叉树
二叉树的层序遍历
二叉树的最大深度
从前序与中序遍历序列构造二叉树
只出现一次的数字
二叉树展开为链表
最长连续序列
单词拆分
环形链表
环形链表2
排序链表
相交链表
乘积最大子数组
最小栈
多数元素
打家劫舍
数组中重复的数据
二叉树的中序遍历
卖股票的最佳时机
数组中的第K个最大元素

  1. Articles
  1. More
  2. 40-环形链表2.mdx
目录
题目思路1 哈希存储代码思路2 快慢指针代码
2041 字
约 6 分钟
更新于 2026/08/20

环形链表 II

题目

给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。
不允许修改 链表。

示例1:
示例1

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

示例二: 示例二

输入:head = [1,2], pos = 0
输出:返回索引为 0 的链表节点
解释:链表中有一个环,其尾部连接到第一个节点。

示例3:
示例3

输入:head = [1], pos = -1
输出:返回 null
解释:链表中没有环。

思路1: 哈希存储

遍历链表,利用 Map 建立映射,每次遍历之前判断 Map 中是否已经存储过当前链表节点,
如果已经存过,则返回该节点;
如果没有存储,则存储该节点,继续向下遍历

代码

var detectCycle = function (head) {
    const map = new Map();
    while(head) {
        if(map.has(head)) {
            return map.get(head);
        }
        map.set(head, head)
        head = head.next;
    }
    return null
};

思路2: 快慢指针

  1. 声明两个指针,分别是 fast 和 slow 两个指针都指向头节点 head , 然后向后移动,fast 移动的速度是 slow 的两倍.
  2. 当 fast 和 slow 相遇时,说明链表有环。 推导过程如下:
    • 假设链表大致形状如下图所示: 环形链表2
      • 链表 head 到 入环节点 的长度为 a

      • slow 进入环后,又走了 b ,然后与 fast 相遇。此时假设换剩下的长度为 c 此时 fast 已经走过了 n 圈环才和 slow 相遇,即走过了 n(b+c)

        • fast 走过的距离为: a + b + n(b+c)
        • slow 走过的距离为: a + b

        因为 fast 的速度是 slow 的两倍,所以 fast 走过的距离是 slow 走过的两倍,
        即: a + b + n(b+c) = 2(a + b)
        转换得: a = c + (n - 1)(b + c) 结合等式和图:
        如果此时有一个指针 current 从 head 向后移动,同时 slow 也向后继续移动,那么当 current 走过距离 c 的时候,距离入环节点位置还有距离 a - c,此时 slow 刚好走到入环节点。 那么 a - c = (n - 1)(b + c), 即:current 走到入环节点的距离,等于环长度的 n - 1 倍。那只需要继续向后移动 current 和 slow 指针,直到两者相遇,那么相遇点,就是入环节点。

代码

var detectCycle = function(head) {
    if (!head || !head.next) return null
    let fast = head
    let slow = head
    let isCycle = false
    while (fast && fast.next) {
        slow = slow.next
        fast = fast.next.next
        if(slow === fast) {
            isCycle = true
            break;
        }
    }
    if (!isCycle) {
        return null
    }
 
    let current = head
    while(current !== slow) {
        slow = slow.next
        current = current.next
    }
 
    return current
 
};