zoukankan      html  css  js  c++  java
  • floy判圈算法--龟兔赛跑

    一、算法简述


    Floyd判圈算法(Floyd Cycle Detection Algorithm),又称龟兔赛跑算法(Tortoise and Hare Algorithm),是一个可以在有限状态机、迭代函数或者链表上判断是否存在环,以及判断环的起点与长度的算法。


    二、基本思路


    在某种关系下,顶点 i 到 k 拓扑有序,顶点 k 到 j 也是相同的顺序,那么 i 和 j 也存在这个顺序。要是某一个顶点出现了自己到自己的环,那么图中就有环,但是这种方法复杂度高一些,没有检测顶点出度或者DFS的方法快,但是非常简单。


    三、问题


    如何检测一个链表是否有环,如果有,那么如何确定环的起点和环的长度。


    四、解决方案


    1)判断是否有环?

    龟兔解法的基本思想可以用我们跑步的例子来解释,如果两个人同时出发,如果赛道有环,那么快的一方总能追上慢的一方。进一步想,追上时快的一方肯定比慢的一方多跑了几圈,即多跑的路的长度是圈的长度的倍数。


    基于上面的想法,Floyd用两个指针,一个慢指针(龟)每次前进一步,快指针(兔)指针每次前进两步(两步或多步效果时等价的,只要一个比另一个快就行)。如果两者在链表头以外的某一点相遇(即相等)了,那么说明链表有环,否则,如果(快指针)到达了链表的结尾,那么说明没坏。


    (2)求环的长度

    相遇的时候,一定已经在环上了,然后两个人只要再次在环上接着跑,再次相遇的时候(也就是所谓的套圈),跑的快的那个人就比跑的慢的人整整多跑了一圈,所以环的长度也就出来了。


    (3)如何确定环的起点

    环的检测用上述原理,接下来我们来看一下如何确定环的起点,这也是Floyd解法的第二部分。方法是将其中一个指针移到链表起点,两者同时移动,每次移动一步,那么两者相遇的地方就是环的起点。


    解析:

    首先假设第一次相遇的时候慢指针走过的节点个数为i,设链表头部到环的起点的长度为m,环的长度为n,相遇的位置与起点与起点位置距离为k。

    于是有:

    i = m + a * n + k

    其中a为慢指针走的圈数。


    因为快指针的速度是慢指针的2倍,于是又可以得到另一个式子:

    2 * i = m + b * n + k

    其中b为快指针走的圈数。


    两式相减得:

    i = ( b - a ) * n

    也就是说i是圈长的整数倍。


    这是将其中一个节点放在起点,然后同时向前走m步时,此时从头部走的指针在m位置。而从相遇位置开始走的指针应该在距离起点i+m,i为圈长整数倍,则该指针也应该在距离起点为m的位置,即环的起点。


    实例程序如下:

    [cpp]
      1. int *head = list.GetHead();    
      2. if (head != null) {    
      3.     int *fastPtr = head;    
      4.     int *slowPtr = head;    
      5.     
      6.     bool isCircular = true;    
      7.     
      8.     do     
      9.     {    
      10.         if (fastPtr->Next == null || fastPtr->Next->Next == null) //List end found    
      11.         {    
      12.             isCircular = false;    
      13.             break;    
      14.         }    
      15.     
      16.         fastPtr = fastPtr->Next->Next;    
      17.         slowPtr = slowPtr->Next;    
      18.     } while (fastPtr != slowPtr);    
      19.     //确定环的起点    
      20.     slowPtr = head;    
      21.     while(slowPtr != fastPtr)    
      22.     {    
      23.         slowPtr = slowPtr->Next;    
      24.         fastPtr = fastPtr->Next;    
      25.     }    
      26.     cout<<"the starting point of the cycle is "<<slowPtr<<endl;    
      27. }   
  • 相关阅读:
    A1023 Have Fun with Numbers (20分)(大整数四则运算)
    A1096 Consecutive Factors (20分)(质数分解)
    A1078 Hashing (25分)(哈希表、平方探测法)
    A1015 Reversible Primes (20分)(素数判断,进制转换)
    A1081 Rational Sum (20分)
    A1088 Rational Arithmetic (20分)
    A1049 Counting Ones (30分)
    A1008 Elevator (20分)
    A1059 Prime Factors (25分)
    A1155 Heap Paths (30分)
  • 原文地址:https://www.cnblogs.com/fenglongyu/p/7744537.html
Copyright © 2011-2022 走看看