第4章|链表

Unit 07 · 快慢指针判断链表是否有环

← Unit 06
仅本次页面有效
U07
Linked List CycleIdentity · Relative Speed · Safe Exit

环不是“值重复”,
而是沿 next 再次回到同一个节点。

本单元把 Unit 06 的快慢指针从“定位中点”迁移为“识别循环”。真正需要观察的不是节点值,而是两个指针是否引用同一个节点对象。

检测到上次学习记录可以恢复阶段、动画、代码、脚手架和滚动位置。
LeetCode #141 题目模型判断是否存在沿 next 可重复到达的节点

题目要求

给定单链表头节点 head,判断链表中是否存在环。若某个节点能够通过不断沿 next 再次到达自身,则链表有环。

class Solution: def hasCycle( self, head: Optional[ListNode] ) -> bool: pass

重要说明

  • pos 只用于描述尾节点连接到哪个位置,不作为函数参数;
  • 节点值可以重复,不能依赖 val 判断是否相遇;
  • 目标是返回布尔值,不需要定位环入口。

示例

head = [3,2,0,-4], pos = 1
输出:True
尾节点 -4 的 next 指向索引 1 的节点 2。
head = [1,2], pos = 0
输出:True
head = [1], pos = -1
输出:False

数据范围

节点数量不超过 10⁴;节点值可能重复;空链表和单节点无环链表都必须安全处理。

与 Unit 06 的联系

Unit 06:中间结点

速度比 2:1,把链表长度的一半编码到 slow 的位置。

→
Unit 07:环形链表

速度比仍是 2:1,但关注环内相对距离是否最终归零。

Cycle 环

沿 next 连续前进时,会再次访问某个已经访问过的节点对象。

Identity 身份

两个变量引用同一节点对象。值相等不代表身份相同。

Relative Speed 相对速度

fast 每轮比 slow 多走一步;进入环后,两者环上距离每轮变化 1。

Safe Exit 安全退出

若无环,fast 或 fast.next 会先到 None;双重判空避免访问 None.next。

认知任务 1
先判断:什么情况才叫“链表有环”?
不要先看快慢指针。先区分节点值重复与节点身份重复。
链表状态动画节点位置只是布局,真正结构由 next 决定
尾节点重新指向 n2
slown1
fastn1
比较身份未比较
循环条件未检查
当前结论未判断

已访问身份 / 环上相对距离

错误依据slow.val == fast.val ?
正确依据slow is fast ?
初始化 slow 与 fast,它们都指向 head。
检查边界
slow 走 1
fast 走 2
比较身份
返回结果
阶段 1 / 6可随时返回,仅本次页面有效;完成后不会自动跳题
关键表达式补全只填写 7 个关键位置
from typing import Optional class Solution: def hasCycle(self, head): slow = fast = while : slow = fast = if : return True return
分层提示
进入函数。
slow—
fast—
slow.val—
fast.val—
身份相同False
返回状态未返回
完成代码联动后进入官方迁移题:LeetCode #202 快乐数仅代码正确或播放完整轨迹后解锁;查看提示与填入骨架不会解锁。

迁移题:LeetCode #202《快乐数》

把显式链表中的 node.next 抽象成确定性状态转移 next_value(x)。迁移的核心不是复制代码,而是判断:状态是否会到达成功出口 1,还是进入一个不含 1 的循环。

202. Happy Number · Easy

给定正整数 n,不断把它替换为各位数字平方和。若过程最终到达 1,则返回 True;若进入一个不包含 1 的循环,则返回 False。

class Solution: def isHappy(self, n: int) -> bool: pass
输入:n = 19 输出:true 过程:19 → 82 → 68 → 100 → 1
输入:n = 2 输出:false

约束与输出契约

  • 1 ≤ n ≤ 2³¹ - 1;
  • 每一步由“各位数字平方和”唯一确定;
  • 返回布尔值,不需要输出状态序列;
  • 到达 1 是成功出口;重复非 1 状态说明进入失败环。
题面、示例与约束来自 LeetCode 官方页面。打开官方原题 ↗

读题检查 1|19 为什么是快乐数?

先识别题目的成功出口。

读题检查 2|若重复出现非 1 状态,意味着什么?

确定性转移中,相同状态会产生相同后继。

#141 与 #202:同一种“确定性状态链”,不同的状态身份

维度#141 环形链表#202 快乐数
状态节点对象身份当前整数值
一步转移node = node.nextx = next_value(x)
重复判据slow is fastslow == fast
成功出口无;只判断是否有环fast == 1
无环/失败出口fast 到达 None → False在到达 1 前相遇 → False

为什么 #202 可以比较整数值,而 #141 必须比较节点身份?

关注“什么信息足以唯一决定下一状态”。

把 Floyd 环检测映射到数字状态链

槽位 1|生成下一状态

从整数中逐位取数并累加平方。

槽位 2|制造初始速度差

避免 slow 与 fast 在移动前因同为 n 而立即相等。

槽位 3|继续条件

尚未到达成功出口,并且快慢状态尚未相遇。

槽位 4|速度更新

slow 一次转移,fast 两次转移。

四个槽位共同定义“状态、出口、相对速度和相遇”。

官方样例轨迹|n = 19

初始化 slow=19,fast=82,先制造速度差。
1 / 4
slow19
fast82
循环条件True
当前结论继续

独立重写|不要再填占位符

基于刚才的语义映射,独立写出 Floyd 版本。检查器只验证关键结构,不会自动填入答案。

先把官方样例运行到 1,再检查独立重写。
迁移分层提示
完成后停留在本页

可以继续复看显式环与隐式环的差异,或手动进入 Unit 08。

手动进入 Unit 08 →
Unit 07 已完成:你已经从显式链表环迁移到隐式状态环。页面不会自动跳转。
触发信号沿确定的 next 关系可能永远无法到达终点
核心表示slow 走 1 步,fast 走 2 步
循环不变量进入环后 fast 每轮相对 slow 多走 1 步
正确比较slow is fast,比较节点身份
安全边界while fast and fast.next
迁移方向隐式状态环、快乐数、重复状态检测