弗洛伊德判圈算法核心是用slow(步长1)和fast(步长2)双指针遍历单链表,若相遇则有环,若fast遇nullptr则无环;C++实现需先判空和单节点,循环条件为fast&&fast->next,移动后立即比较指针是否相等。
它不依赖额外空间,只用两个指针——slow 和 fast,在单链表上同步移动:slow 每次走 1 步,fast 每次走 2 步。如果链表有环,二者必在环内相遇;若 fast 先走到 nullptr,说明无环。
注意边界:空链表或仅一个节点时,fast->next 可能非法,必须先检查 fast 和 fast->next 是否为 nullptr。
struct ListNode {
int val;
ListNod
e *next;
ListNode(int x) : val(x), next(nullptr) {}
};
bool hasCycle(ListNode *head) {
if (!head || !head->next) return false;
ListNode *slow = head;
ListNode *fast = head;
while (fast && fast->next) {
slow = slow->next;
fast = fast->next->next;
if (slow == fast) return true;
}
return false;
}
走两步是效率与正确性平衡的结果:
fast 走 3 步,可能跳过 slow,导致一次循环内不相遇,需更多轮才能捕获,但不破坏正确性fast 必然在有限步内追上 slow(数学上可证最多绕环一圈就相遇)fast->next->next->next),且无性能收益最常出错的是循环条件和移动顺序:
while (fast->next && fast) —— 顺序反了,fast 为 nullptr 时访问 fast->next 直接段错误if (slow == fast) 放在移动之后但没处理初始重合)—— 若链表只有一个节点且自环(head->next == head),初始 slow == fast,但未进循环就被跳过fast && fast->next,且每次移动后立即判断相等环检测本身不难,但指针操作的边界稍一松懈就会 crash,尤其是和 LeetCode 测试用例里各种极端结构(空、单节点、自环、长链+小环)打交道时,条件顺序和判空缺一不可。