Floyd判圈算法

Floyd判圈算法(Floyd Cycle Detection Algorithm),又稱龜兔賽跑算法(Tortoise and Hare Algorithm),是一個可以在有限狀態機迭代函數或者鍊表上判斷是否存在,求出該環的起點與長度的算法。該算法據高德納稱由美國科學家羅伯特·弗洛伊德發明,但這一算法並沒有出現在羅伯特·弗洛伊德公開發表的著作中[1]頁面存檔備份,存於網際網路檔案館)。

如果有限狀態機、迭代函數或者鍊表上存在環,那麼在某個環上以不同速度前進的2個指針必定會在某個時刻相遇。同時顯然地,如果從同一個起點(即使這個起點不在某個環上)同時開始以不同速度前進的2個指針最終相遇,那麼可以判定存在一個環,且可以求出2者相遇處所在的環的起點與長度。

算法

算法描述

如果有限狀態機、迭代函數或者鍊表存在環,那麼一定存在一個起點可以到達某個環的某處(這個起點也可以在某個環上)。

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

接着,同時讓t和h往前推進,但是二者的速度不同:t每前進1步,h前進2步。只要二者都可以前進而且沒有相遇,就如此保持二者的推進。當h無法前進,即到達某個沒有後繼的節點時,就可以確定從S出發不會遇到環。反之當t與h再次相遇時,就可以確定從S出發一定會進入某個環,設其為環C。

如果確定了存在某個環,就可以求此環的起點與長度。

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

為了求出環C的起點,只要令h仍均位於節點M,而令t返回起點節點S,此時h與t之間距為環C長度的整數倍。隨後,同時讓t和h往前推進,且保持二者的速度相同:t每前進1步,h前進1步。持續該過程直至t與h再一次相遇,設此次相遇時位於同一節點P,則節點P即為從節點S出發所到達的環C的第一個節點,即環C的一個起點。

偽代碼

 1  t := &S
 2  h := &S                                        //令指針th均指向起點節點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

算法複雜度

時間複雜度

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

空間複雜度

僅需要創立指針t、指針h,保存環長n、環的一個起點P。空間複雜度為 ,是常數空間的算法。[3]

應用

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

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

相關算法

雖然Floyd判圈算法已經達到了線性時間複雜度和常數空間複雜度,但是Brent判圈算法將減小時間複雜度的常數係數,平均消耗時間比Floyd判圈算法少36%。[4]

參考鏈接