非常游戏网
如何在Python中检测单链表中是否存在环_使用快慢指针(Floyd判圈算法)

如何在Python中检测单链表中是否存在环_使用快慢指针(Floyd判圈算法)

2026-05-29日常编程226945

快慢指针能检测环是因为有环时相对速度为1,有限步内必相遇;无环时快指针先达None。需检查空链表、单节点、自环等边界条件,循环条件应为while fast and fast.next,用is比较节点,相遇后可求环入口。

快慢指针为什么能检测环

核心在于:如果链表有环,快指针(每次走2步)迟早会追上慢指针(每次走1步);如果无环,快指针必然先走到 None。这不是概率问题,而是数学上的相遇必然性——环内相对速度为1步/轮,有限步数内必相遇。

常见误判场景:链表为空、只有一个节点、头节点指向自己(自环)。这些都要在循环前单独检查,否则 fast.nextfast.next.next 会抛 AttributeError: 'NoneType' object has no attribute 'next'

标准实现中必须检查的边界条件

标准写法里,循环条件不能只写 while fast and slow,而要确保每次访问 fast.nextfast 不为 None,且访问 fast.next.nextfast.next 也不为 None。推荐统一用这个条件:

while fast and fast.next:

这样能安全执行 fast = fast.next.next。同时注意:初始化时若 headNone,直接返回 False;若 head.next is head,第一次循环就能捕获。

容易踩的坑:

  • slow = slow.next 放在 fast = fast.next.next 前面——顺序不影响逻辑,但若提前移动 slow,可能错过起点处的自环判断
  • 循环内先比较再移动——正确;但有人写成先移动再比较,会导致空指针异常或漏判长度为2的环(如 A→B→A)
  • is 比较节点(正确),绝不能用 ==,因为节点值可能重复,但地址唯一

如何从相遇点找到环入口(进阶需求)

很多面试会追问:找到环了,那环的入口节点在哪?这需要额外数学推导:设头到入口距离为 a,入口到相遇点为 b,环剩余部分为 c,则有 2(a+b) = a + b + n(b+c),推出 a = n(b+c) - b = (n-1)(b+c) + c。也就是说,从头和从相遇点各出发一个指针,每次走1步,它们必在入口相遇。

Python 3.14.3

微软官方的 Python 扩展,是 VS Code 安装量最高的扩展(209M+)。集成 IntelliSense(通过 Pylance)、调试(通过 Python Debugger)、代码检查、格式化、重构和单元测试等功能。支持 Jupyter Notebook、虚拟环境管理和多 Python 版本切换。

实操步骤:

  • 先用快慢指针确认有环,并停在相遇点
  • 初始化 ptr1 = headptr2 = meet_node
  • while ptr1 is not ptr2: ptr1 = ptr1.next; ptr2 = ptr2.next
  • 返回 ptr1

注意:这个推导依赖快慢指针首次相遇时,慢指针还没绕满一圈——而 Floyd 算法天然满足这点,所以可直接用。

Python里没有指针,但节点引用足够用

Python 中的 “节点” 本质是对象引用,node.next 是对另一个对象的强引用。只要两个变量指向同一个节点实例,a is b 就为 True,完全满足环检测所需的地址等价判断。

不需要手动模拟指针或用 id() 比较——那既慢又不可靠(id 可能复用)。直接用 is 最简洁安全。

真正容易被忽略的是:环检测只能告诉你“有环”,但无法区分是逻辑错误(比如误将旧节点插入新链表)还是设计使然(比如某些缓存结构故意成环)。上线前得结合业务语义做二次判断。