判圈算法(Flyod、Brent's)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u011221820/article/details/78821464

问题

如何检测一个链表是否有环?如果有环,如何确定环的起点以及长度?

Floyd cycle detection(龟兔算法)

龟兔赛跑

对于赛道来说,如果赛道中有环,那么速度快的兔子一定会在某个地点追上乌龟,并且兔子所跑的距离减去乌龟所跑的距离,一定是环长度的整数倍。

原理

假设令龟、兔为指针,并且指向起点位置,兔子每次移动两个节点,乌龟每次移动一个节点。如果两者在起始节点外相遇,则说明有环。如果兔子在走到链表尾部还没有与乌龟相遇,说明无环。

环长度

通过上述算法判断出存在环C时,显然龟兔位于同一节点M,此时,令兔子保持不动,而乌龟不断推进,记录移动距离,等再次相遇时,移动步数即环C长度。

环起点

只要令兔子仍位于相遇节点M, 而令乌龟返回链表起始节点S,此时乌龟与兔子之间的距离为环C长度的整数倍。随后,同时让乌龟与兔子不断推进一步,直至相遇同一节点P,则节点P即为从链表起始节点所到达的环C的第一个节点,即环C起点。

分析

如图所示,令起始节点为h, 起始节点到环起点距离为m, 龟兔相遇节点为pp节点与环起点距离为n
当龟兔相遇时
乌龟所跑距离为 S = m + n + aC(C 为环长度),
兔子所跑距离为2S = m + n + bC(因为兔子速度为乌龟2倍)
故得S = (b - a)C = m + n + aC ==> (b - 2a)C = m + n, 由此可知,m + n 为环C长度整数倍.
那么令乌龟返回链表起始节点,兔子仍在相遇节点P,两者同时不断推进直至相遇,相遇节点为环起点。因为乌龟移动距离m, 则兔子也移动距离m, 原本p 节点距离环起点为n, 由于m + n 为环长整数倍,故兔子移动到了环起点,即相遇节点为环起点。
Floyd Circle Detection

int *t = head, *h = head;
// 判圈
do{
    h = h->next;
    if(h != null)
        h = h->next;
    t = t->next;
}while(h != t || h != null);

if(h != null)
{
    // 环长度
    t = head;
    cnt = 0;
    while(h != t)
    {
        t = t->next;
        ++cnt;
    }
    // 环起点
    t = head;
    while(h != t)
    {
        h = h->next;
        t = t->next;
    }
    p = h; // p为起点
}

算法复杂度

时间复杂度

如果乌龟走到环起点P时,此时显然兔子已经在环内某节点,之后兔子最多走一圈就会与乌龟相遇。假设链表起始节点到环起点距离为m, 环长度为n, 故时间复杂度为O(m + n)

空间复杂度

算法仅需要创建指针t, h 环长计数n 以及环起点P. 故空间复杂度为O(1)

Brent’s Cycle Detection(The Teleporting Turtle)

原理

起始令乌龟兔子指向起始节点,让乌龟保持不动,兔子走2i 步, 即迭代一次,兔子走2步, 迭代2次,兔子走4步等等。在兔子走每一步后,判断龟兔是否相遇,如果没有相遇,则让乌龟的位置变成兔子所在的位置,否则如果乌龟不动,则乌龟永远无法进入环内,随后进入新的迭代。循环下去,直至相遇或到表尾。

环长

由于乌龟一直处于兔子更改步长上限时的位置,所以更改步长上线后,兔子走了几步与乌龟相遇,则环的长度就为多少。

环起点

Flyod判圈算法利用了乌龟和兔子的距离是环长整数倍的性质求起点,所以可以让乌龟回到起点,兔子回到距离起点环长距离的节点,随后与Floyd算法一样

int *t = head, *h = head;
int steps_taken = 0, steps_limit = 2;
while(True)
{
    if(h == null)
        break; // No Loop
    ++steps_taken;
    if(h == t)
        break; // Found Loop;
    if(steps_taken == step_limit)
    {
        steps_taken = 0;
        step_limit *= 2;
        t = h; // teleport the t;
    }
}

算法复杂度

时间复杂度

O(n), 线性复杂度,并且比Flyod 表现更好,且Flyod 是该算法的最差表现

空间复杂度

O(1) 常数空间

参考资料

WIKIPEDIA Cycle detection
Floyd判圈算法
Floyd’s Cycle Detection Algorithm
Brent’s Cycle Detection Algorithm

展开阅读全文

没有更多推荐了,返回首页