Floyd算法——Cycle detection

今天在寝室看了POJ 上一道拓扑排序的题POJ1094 到实验室之后正好被欧阳看到,跟我说了另外一种算法Floyd Warshall 具体参见 http://en.wikipedia.org/wiki/Floyd–Warshall_algorithm 其实就是求任意两点间的最短路径的Floyd算法,这个也能拓展到闭包问题上去。
基本思路就是:在某种关系下,顶点i 到k 拓扑有序,顶点k 到 j也是相同的顺序,那么 i 和 j 也存在这个顺序。要是某一个顶点出现了自己到自己的环,那么图中就有环,但是这种方法复杂度高一些,没有检测顶点出度或者DFS的方法快,但是非常简单。
let dist be a |V| × |V| array  initialized to 0
 for each vertex v
   dist[v][v] ← 0
 for each edge (u,v)
   dist[u][v] ← 1  // u,v in order
 for k from 1 to |V|
   for i from 1 to |V|
      for j from 1 to |V|
         if dist[i][k]  && dist[k][j]  then
            dist[i][j]  // i,j in order
 
 for i from 1 to |V|
     if dist[i][i] // self circle
        // circle detected


不经意发现了链接上有另外一个问题:如何判断链表中是否有环,环的起点和环的长度如何确定。http://en.wikipedia.org/wiki/Floyd's_cycle-finding_algorithm#Tortoise_and_hare 这里面有很详细的介绍,但是我没怎么看懂(英文太差)后来搜了一下,发现一篇博客上讲的很好。 

问题:如何检测一个链表是否有环,如果有,那么如何确定环的起点.

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

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

环的检测从上面的解释理解起来应该没有问题。接下来我们来看一下如何确定环的起点,这也是Floyd解法的第二部分。方法是将慢指针(或快指针)移到链表起点,两者同时移动,每次移动一步,那么两者相遇的地方就是环的起点。

   这样做的道理我用下图解释。假设起点到环的起点距离为m,已经确定有环,环的周长为n,(第一次)相遇点距离环的起点的距离是k。那么当两者相遇时,慢指针移动的总距离为i,i = m + a * n + k,因为快指针移动速度为慢指针的两倍,那么快指针的移动距离为2i,2i = m + b * n + k。其中,a和b分别为慢指针和快指针在第一次相遇时转过的圈数。我们让两者相减(快减慢),那么有i = (b - a) * n。即i是圈长度的倍数。利用这个结论我们就可以理解Floyd解法为什么能确定环的起点。将一个指针移到链表起点,另一个指针不变,即距离链表起点为i处,两者同时移动,每次移动一步。当第一个指针前进了m,即到达环起点时,另一个指针距离链表起点为i + m。考虑到i为圈长度的倍数,可以理解为指针从链表起点出发,走到环起点,然后绕环转了几圈,所以第二个指针也必然在环的起点。即两者相遇点就是环的起点。


 

Python:
def floyd(f, x0):
   # The main phase of the algorithm, finding a repetition x_mu = x_2mu
   # The hare moves twice as quickly as the tortoise
   # Eventually they will both be inside the cycle 
   # and the distance between them will increase by 1 until
   # it is divisible by the length of the cycle.
   tortoise = f(x0) # f(x0) is the element/node next to x0.
   hare = f(f(x0))
   while tortoise != hare:
       tortoise = f(tortoise)
       hare = f(f(hare))

   # at this point the position of tortoise which is the distance between 
   # hare and tortoise is divisible by the length of the cycle. 
   # so hare moving in circle and tortoise (set to x0) moving towards 
   # the circle will intersect at the beginning of the circle.

   # Find the position of the first repetition of length mu
   # The hare and tortoise move at the same speeds
   mu = 0
   tortoise = x0
   while tortoise != hare:
       tortoise = f(tortoise)
       hare = f(hare)
       mu += 1

   # Find the length of the shortest cycle starting from x_mu
   # The hare moves while the tortoise stays still
   lam = 1
   hare = f(tortoise)
   while tortoise != hare:
       hare = f(hare)
       lam += 1

   return lam, mu


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值