跳到正文
第 141 题简单

环形链表

Linked List Cycle

判断一条链表里是否存在环(某个节点的 next 指回了之前的节点)。

在力扣中练习

题目描述

给你一个链表的头节点 head ,判断链表中是否有环。

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

如果链表中存在环 ,则返回 true 。 否则,返回 false 。

 

示例 1:

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

示例 2:

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

示例 3:

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

 

提示:

  • 链表中节点的数目范围是 [0, 104]
  • -105 <= Node.val <= 105
  • pos 为 -1 或者链表中的一个 有效索引 。

 

进阶:你能用 O(1)(即,常量)内存解决此问题吗?

解题思路

主解法

快慢指针 · Floyd 判环

在环里,跑得快的那个一定追得上慢的

最直接的办法是沿途记录访问过的节点,一旦重复就说明有环;但这需要 O(n) 的哈希集合。

Floyd 判环只用两个指针:slow 每次走一步,fast 每次走两步。如果链表有终点,fast 会先到 null;如果有环,fast 会先进入环,然后在环里一圈圈追 slow。

为什么一定追得上?进入环后,fast 每轮比 slow 多走一步,两者距离每轮减少 1,最多经过环长那么多轮就会重合。slow 不需要跑满一圈。

分步图解

第 1 轮:slow → node-1,fast → node-2,还没有相遇。

尾节点指回链中,形成环值 · next 引用
7#0节点 #0,值 7,next → node-14#1节点 #1,值 4,next → node-2slow9#2节点 #2,值 9,next → node-3fast2环入口节点 环入口,值 2,next → node-46#4节点 #4,值 6,next → node-51#5节点 #5,值 1,next → node-3
1 / 3
fast 每次多走一步,环内的距离逐轮缩小,最后一定重合。

自拟主例的环入口在下标 3,fast 和 slow 进入环的时间不同;画面上两个指针最终在同一节点重合,而不是在入口处相遇。题目只要求判断有没有环,所以第一次重合就可以返回。

时间 O(n)空间 O(1)

易错点

  • 只判断 fast 为空:fast->next 也可能为空,取 fast->next->next 会崩溃。
  • 先移动再判断时漏掉初始重合:本题初始都在 head,不能一上来就返回 true。
  • 把“值相等”当成相遇:必须比较节点地址。
对照解法

哈希集合 · 记录访问

走过的节点记下来,第二次遇到就是环

逻辑最直白,但需要 O(n) 额外空间;主解法用两个指针把空间降到 O(1)。

从 head 开始遍历,把每个节点地址放进 unordered_set。如果当前节点已经在集合里,说明之前来过,存在环;如果走到 null,说明没有环。

它不依赖快慢指针的追及证明,非常适合快速验证。代价是额外 O(n) 的集合空间,而且需要能对节点地址做哈希。

如果题目还要求返回环入口,这个方案第一次重复访问的节点就是入口。

  1. 1建立空的节点集合。
  2. 2沿 next 遍历,检查当前节点是否出现过。
  3. 3重复出现返回 true;走到 null 返回 false。
时间 O(n)空间 O(n)

易错点

  • 存节点的值而不是地址:不同节点可能值相同,会误判。
  • 遍历条件写成 p->next:会漏掉尾节点本身。

代码与演示

主解法配有图解;对照解法用于比较思路、复杂度或实现方式。

演示输入head = [7,4,9,2,6,1],1 的 next 指回 2

slow、fast 都从头出发;fast 每轮走两步,slow 走一步。

尾节点指回链中,形成环值 · next 引用
7#0节点 #0,值 7,next → node-1slowfast4#1节点 #1,值 4,next → node-29#2节点 #2,值 9,next → node-32环入口节点 环入口,值 2,next → node-46#4节点 #4,值 6,next → node-51#5节点 #5,值 1,next → node-3
1 / 3
查看 C++ 代码

延伸阅读

  • Floyd 判环是链表题里少数不借助额外空间的“追及”技巧,后面找环入口会继续用到。
  • 快指针走两步、慢指针走一步只是最常见配速;关键是两者在环内的相对速度非零。