(/≧▽≦)/~┴┴ 嗨~我叫小奥 ✨✨✨
👀👀👀 个人博客:小奥的博客
👍👍👍:个人CSDN
⭐️⭐️⭐️:Github传送门
🍹 本人24应届生一枚,技术和水平有限,如果文章中有不正确的内容,欢迎多多指正!
📜 欢迎点赞收藏关注哟! ❤️
文章目录
一、基础概念
Floyd判圈算法(Floyd Cycle Detection Algorithm),又称龟兔赛跑算法(Tortoise and Hare Algorithm),是一种用于检测有限状态机、迭代函数或链表结构中是否存在环路的算法。该算法不仅能判断环的存在性,还可确定环的起点位置及其长度。
算法采用双指针策略,通过不同移动速度的指针遍历数据结构,根据指针相遇情况判定环路特征。
- 时间复杂度 O(n)
- 空间复杂度 O(1)
初始化:
- 快慢指针:
fast = slow = head
- 单列表节点定义:
python
from typing import Optional
class Node:
"""单链表节点定义"""
__slots__ = ("val", "next")
def __init__(self, val: int, nxt: "Optional[Node]" = None):
self.val = val
self.next = nxt
二、检测是否有环
- 慢指针每次前进一步:
slow = slow.next
- 快指针每次前进两步:
fast = fast.next.next
- 快慢指针相遇(
slow == fast
),则链表中必含环;否则无环。
为什么有环快慢指针一定能相遇?
若存在环,快指针相对于慢指针速度为1步/轮,必然会在环内相遇。
python
# -------------------------------------------------
# 1. 探测是否有环(返回相遇节点或 None)
# -------------------------------------------------
def detect_cycle(head: Optional[Node]) -> Optional[Node]:
"""Floyd 第一阶段:龟兔赛跑"""
slow = fast = head
while fast and fast.next:
slow = slow.next # 1 步
fast = fast.next.next # 2 步
if slow is fast: # 指针同一对象
return slow # 返回相遇点
return None # 无环
三、查找环的入口
- 相遇后,让慢指针
slow
回到头结点,另一指针fast
停在相遇点:slow=head
- 之后让慢指针
slow
继续走,并从头结点开始记录距离:slow=slow.next, head=head.next
- 两个结点再次相遇的节点为环的入口
python
# -------------------------------------------------
# 2. 定位环入口(若无环返回 None)
# -------------------------------------------------
def find_cycle_entry(head: Optional[Node]) -> Optional[Node]:
meet = detect_cycle(head)
if meet is None:
return None
# 第二阶段:同步一步一步走
slow = head
while slow is not meet:
slow = slow.next
meet = meet.next
return slow # 或 meet,二者同一节点
四、求环的长度
方法 1:单指针绕环一周(最常用)
- 任选环内一个节点,记为 p(可用入口节点,也可用第一次相遇点)。
- 令 q = p.next,计数器 cnt = 1。
- 当 q ≠ p 时循环
q = q.next;cnt++。 - 返回 cnt。
结论 :cnt 就是环长度 L。
时间 O(L),空间 O(1)。
------ 这就是最简洁的"公式" :L = 单次遍历回到起点的步数。
方法 2:双指针再跑一次"龟兔"------一次就同时求出 L 与验证
(如果忘记保存入口节点,或者想再练一次快慢指针,可以用这个变种)
- 在第一次相遇点后,保持慢指针不动,把快指针重新指向相遇点。
- 快指针每次走 1 步,计数器 cnt = 0。
- 循环直到两指针再次相遇:
fast = fast.next;cnt++。 - 返回 cnt。
结论 :cnt 就是 L。
原理:相对速度为 0,快指针恰好多跑一整圈。
python
# -------------------------------------------------
# 3. 计算环长度(若无环返回 0)
# -------------------------------------------------
def cycle_length(head: Optional[Node]) -> int:
meet = detect_cycle(head)
if meet is None:
return 0
# 方法 1:单指针绕环一周
# cur, cnt = meet.next, 1
# while cur is not meet:
# cur = cur.next
# cnt += 1
# return cnt
# 方法 2:把 fast 重新指向 meet,两者都一次一步
slow = meet
fast = meet.next # 相对距离 1
cnt = 1 # 已经隔开 1 步
while slow is not fast:
fast = fast.next # 每次一步
cnt += 1
return cnt