1. 程式人生 > >龜兔賽跑演算法(Floyd判圈演算法)

龜兔賽跑演算法(Floyd判圈演算法)

參考資料:

1. 維基百科

https://zh.wikipedia.org/w/index.php?title=Floyd判圈演算法&redirect=no

2. http://blog.csdn.net/thestoryofsnow/article/details/6822576

Floyd判圈演算法(Floyd Cycle Detection Algorithm),又稱龜兔賽跑演算法(Tortoise and Hare Algorithm),該演算法由美國科學家羅伯特·弗洛伊德發明。

演算法作用

可以在有限狀態機、迭代函式或者連結串列上判斷是否存在環,以及求出該環的起點與長度的演算法。

演算法原理

如果存在環,那麼從同一個起點(即使這個起點不在某個環上)處,同時開始以不同速度前進的2個指標必定會在某個時刻相遇。

演算法描述

(1)求環

初始狀態下,假設已知某個起點節點為節點S。現設兩個指標t和h,將它們均指向S。

同時讓t和h往前推進,h的速度為t的2倍),直到h無法前進,即到達某個沒有後繼的節點時,就可以確定從S出發不會遇到環。反之當t與h再次相遇時,就可以確定從S出發一定會進入某個環,設其為環C。(h和t推進的步數差是環長的倍數)

(2)求環的長度

上述演算法剛判斷出存在環C時,t和h位於同一節點,設其為節點M。僅需令h不動,而t不斷推進,最終又會返回節點M,統計這一次t推進的步數,就是環C的長度。

(3)求環的起點

為了求出環C的起點,只要令h仍均位於節點M,而令t返回起點節點S。隨後,同時讓t和h往前推進,且速度相同。持續該過程直至t與h再一次相遇,此相遇點就是環C的一個起點。

why?

假設出發起點到環起點的距離為m,已經確定有環,環的周長為n,(第一次)相遇點距離環的起點的距離是k。那麼當兩者相遇時,慢指標(t)移動的總距離i = m + a * n + k,快指標(h)的移動距離為2i,2i = m + b * n + k。其中,a和b分別為t和h在第一次相遇時轉過的圈數。讓兩者相減(快減慢),那麼有i = (b - a) * n。即i是圈長度的倍數。

將一個指標移到

出發起點S,另一個指標仍呆在相遇節點M處兩者同時移動,每次移動一步。當第一個指標前進了m,即到達環起點時,另一個指標距離連結串列起點為i + m。考慮到i為圈長度的倍數,可以理解為指標從連結串列起點出發,走到環起點,然後繞環轉了幾圈,所以第二個指標也必然在環的起點。即兩者相遇點就是環的起點。

虛擬碼

<span style="font-family:Times New Roman;font-size:14px;"> 1  t := &S
 2  h := &S                                        //令指標t和h均指向起點節點S。
 3  repeat
 4      t := t->next
 5      h := h->next
 6      if h is not NULL                                //要注意這一判斷一般不能省略
 7              h := h->next
 8  until t = h or h = NULL
 9  if h != NULL                                       //如果存在環的話
 10     n := 0
 11     repeat                                              //求環的長度
 12             t := t->next
 13             n := n+1
 14     until t = h
 15     t := &S                                     //求環的一個起點
 16     while t != h
 17             t := t->next
 18             h := h->next
 19     P := *t</span>

演算法複雜度

(1)時間複雜度

注意到當指標t到達環C的一個起點節點P時(此時指標h顯然在環C上),之後指標t最多僅可能走1圈。若設節點S到P距離為m,環C的長度為n,則時間複雜度為O(m+n),是線性時間的演算法。

(2)空間複雜度

僅需要創立指標t、指標h,儲存環長n、環的一個起點P。空間複雜度為O(1),是常數空間的演算法。

應用

對於有限狀態機與連結串列,可以判斷從某個起點開始是否會返回到訪問過執行過程中的某個狀態和節點。

對於迭代函式,可以判斷其是否存在週期,以及求出其最小正週期。