如何判斷鍊錶有環並計算環的長度

2021-06-26 02:26:36 字數 3438 閱讀 2827

總體思路:

給出題目:檢測單鏈表中是否存在環。

可以遍歷這個鍊錶,遍歷過的節點標記為done,如果當目前準備遍歷的節點為done的時候,那麼存在環,否則準備檢測的節點為null時,遍歷完成,不存在環。

附加條件:每個節點是唯讀的,不可以做標記呢?

可以另外開闢乙個陣列,每次遍歷完乙個節點後,儲存這個節點的唯一位址到陣列,如果要遍歷的節點已在陣列中,那麼存在環,要是取到null還沒有重複,那麼就是不存在了,當然這個陣列可以是hash表。

附加條件:只可以另外開闢常數空間呢?

可以使用快慢指標,然後分別每次a指標向後移動1步,b指標向後移動2步,如果a和b指向了同乙個節點那麼存在環,如果有乙個指向了null,那麼不存在環。

附件條件:這個環可以出現在任何地方呢?

不可能,只會在最後!

具體方法:

鍊錶球環路的問題經常出現在面試題中,希望通過下面的解釋能偶掌握這幾個問題。

問題:1、如何判斷乙個鍊錶是不是這類鍊錶?

2、如果鍊錶為存在環,如何算環的長度?

3、如果鍊錶為存在環,如何算柄的長度?

第一問是否有環就用快慢指標,fast=fast->next-next,slow=slow->next;**如下

[cpp]view plain

copy

bool

i***itsloop(slist *head)  

return

!(fast == null || fast->next == null);  

}  

另一種快慢指標的**寫法:(上面的**更簡潔,但下面的**我感覺更容易理解)

int is_cycle_list(node *list) 

while(two_step)

two_step = two_step->next;

if(one_step == two_step)

}return false;

}

這樣就可以判斷是否有環路

判斷是否有環的第二種方法:反轉鍊錶

反轉鍊錶,反轉時,會用到3個指標,分別為指向遍歷時當前的結點的current指標,指向反轉後的子鍊錶的頭結點的指標temp,及指向遍歷時當前結點的下乙個結點的next指標,如果在反轉時,出現了next指向頭結點的情況,那麼肯定是有環的。 

如:1->2->3->4->5->3,反轉時,會有以下步驟: 

①temp = null; current = 1;  next = 2;  此時,反轉生成的子鍊錶:1->null 

②temp = 1;  current = 2;  next = 3;  此時,反轉生成的子鍊錶:2->1->null 

③temp = 2;  current = 3;  next = 4;  此時,反轉生成的子鍊錶:3->2->1->null 

④temp = 3;  current = 4;  next = 2;  此時,反轉生成的子鍊錶:4->3->2->1->null 

⑤temp = 4;  current = 5;  next = 3;  此時,反轉生成的子鍊錶:5->4->3->2->1->null 

⑥temp = 5;  current = 3;  next = 2;  此時,反轉生成的子鍊錶:3->5->4->3 斷開了 2->1->null 

⑦temp = 3;  current = 2;  next = 1;  此時,反轉生成的子鍊錶:2->3->5->4->3 斷開了 1->null 

⑧判斷到了next指向了頭結點,說明有環。 

**為: 

c**  

int is_cycle_list(node* head)   

temp = current;  

current = next;  

next = next->next;  

current->next = temp;  

}  return false;  

}  

第二問

首先引入乙個圖,

鍊錶存在環,則fast和slow兩指標必然會在slow對鍊錶完成一次遍歷之前相遇,證明如下:

slow首次在a點進入環路時,fast一定在環中的b點某處。設此時slow距煉表頭head長為x,b點距a點長度為y,環周長為s。因為fast和slow的步差為1,每次追上1個單位長度,所以slow前行距離為y的時候,恰好會被fast在m點追上。因為y

fast和slow相遇了,可以肯定的是這兩個指標肯定是在環上相遇的。此時,還是繼續一快一慢,根據上面得到的規律,經過環長s,這兩個指標第二次相遇。這樣,我們可以得到環中一共有多少個節點,即為環的長度。

簡言之:第一次相遇後,繼續按照2 1的步數走,再次相遇時,slot走的步數為環的長度。

第三問,求柄的長度:

有人對fast和slow的步長作了不同的設定來改善演算法的效率,其實採用別的步長有可能使兩指標無法在完成第一次遍歷之前相遇,因此步長1和2是乙個最優的選擇。

假設slow行進了x並在a點進入環路時,fast在環中已經行進了n圈來到b點(n>=0),其行進距離為2x,則可得到如下等式:2x = x +ns+s-y,做一下運算,即x=(n+1)s-y

若此時再設定乙個指向頭節點的指標p,而slow在m處,當p行進了x來到a點時,m行進了x=(n+1)s-y,恰好也來到a處,此時,2個指標相遇了。走的步數即為x長度可知。

簡言之:第二次相遇後,fast指標指向head ,按照步長1走,slow指標繼續走,知道fast==slow的時候,走的步數就為柄長度x的長度。

演算法如下:

[cpp]view plain

copy

slist* findloopport(slist *head,

int& cir_length,

int& bing_length)  

if(fast == null || fast->next == null)

//判斷有環

return

null;  

cir_length = 0;     //環長度

while

( fast != slow )   

bing_length = 0;       //環長度

fast = head;  

while

(slow != fast)    

//再次相遇

return

slow;  

}  

經過這些**後,希望能對鍊錶求環的問題有乙個更深入的了解。

參考:

本文整理自所有權力歸原作者所有。

如何判斷鍊錶是否有環?如何計算環的長度?

1.如何判斷是否有環?如果有兩個頭結點指標,乙個走的快,乙個走的慢,那麼若干步以後,快的指標總會超過慢的指標一圈。2.如何計算環的長度?第一次相遇 超一圈 時開始計數,第二次相遇時停止計數。3.如何判斷環的入口點 碰撞點p到連線點的距離 頭指標到連線點的距離,因此,分別從碰撞點 頭指標開始走,相遇的...

判斷鍊錶是否有環,若有,計算環長度

原文 思想 快慢指標法。快指標的步長是2,慢指標的步長是1,若相遇,則必有環。int hascycle listnode head fast head slow head if null fast null fast next return0 環長計算思想 快慢指標從第一次重合到第二次重合,慢指標的...

如何判斷鍊錶是否有環 鍊錶是否有環的判斷

對於鍊錶是否存在環,有三個問題需要考慮 1.是否有環 2.入環節點 3.環的長度 第一種方法快慢指標法,也稱之為龜兔演算法,設定兩個指標,慢指標和快指標。最開始均指向鍊錶的頭節點,之後,快指標每次後移兩個節點,慢指標每次後移乙個節點。1.如果快指標指向空,則鍊錶無環 2.若快指標和慢指標再次指向乙個...