漫畫演算法:如何判斷連結串列有環?

玻璃貓發表於2016-09-30

大四畢業前夕,計算機學院,

正在四處求職的小灰碰到了同系的學霸大黃……

%e5%b0%8f%e4%bb%93%e9%bc%a0%e8%af%a2%e9%97%ae

%e5%b0%8f%e4%bb%93%e9%bc%a0%e5%8f%b9%e6%b0%94

%e5%b0%8f%e4%bb%93%e9%bc%a0%e8%af%a2%e9%97%ae2

%e5%b0%8f%e4%bb%93%e9%bc%a0%e6%b2%89%e6%80%9d

小灰邊說邊回憶著上週去面試的情形……

%e5%b0%8f%e4%bb%93%e9%bc%a0%e9%9d%a2%e8%af%95%e5%ae%981

%e5%b0%8f%e4%bb%93%e9%bc%a0%e8%af%a2%e9%97%ae1

%e5%b0%8f%e4%bb%93%e9%bc%a0%e9%9d%a2%e8%af%95%e5%ae%982

有一個單向連結串列,連結串列當中有可能出現“環”,就像下圖這樣。如何用程式判斷出這個連結串列是有環連結串列?
%e9%93%be%e8%a1%a8%e5%9b%be1

%e5%b0%8f%e4%bb%93%e9%bc%a0%e8%8b%a6%e6%80%9d

%e5%b0%8f%e4%bb%93%e9%bc%a0%e5%9b%9e%e7%ad%942

方法一:首先從頭節點開始,依次遍歷單連結串列的每一個節點。每遍歷到一個新節點,就從頭節點重新遍歷新節點之前的所有節點,用新節點ID和此節點之前所有節點ID依次作比較。如果發現新節點之前的所有節點當中存在相同節點ID,則說明該節點被遍歷過兩次,連結串列有環;如果之前的所有節點當中不存在相同的節點,就繼續遍歷下一個新節點,繼續重複剛才的操作。

例如這樣的連結串列:A->B->C->D->B->C->D, 當遍歷到節點D的時候,我們需要比較的是之前的節點A、B、C,不存在相同節點。這時候要遍歷的下一個新節點是B,B之前的節點A、B、C、D中恰好也存在B,因此B出現了兩次,判斷出連結串列有環。

假設從連結串列頭節點到入環點的距離是D,連結串列的環長是S。那麼演算法的時間複雜度是0+1+2+3+….+(D+S-1) = (D+S-1)*(D+S)/2 , 可以簡單地理解成 O(N*N)。而此演算法沒有建立額外儲存空間,空間複雜度可以簡單地理解成為O(1)。

%e5%b0%8f%e4%bb%93%e9%bc%a0%e9%9d%a2%e8%af%95%e5%ae%983

%e5%b0%8f%e4%bb%93%e9%bc%a0%e8%8b%a6%e6%80%9d

%e5%b0%8f%e4%bb%93%e9%bc%a0%e5%9b%9e%e7%ad%943

方法二:首先建立一個以節點ID為鍵的HashSet集合,用來儲存曾經遍歷過的節點。然後同樣是從頭節點開始,依次遍歷單連結串列的每一個節點。每遍歷到一個新節點,就用新節點和HashSet集合當中儲存的節點作比較,如果發現HashSet當中存在相同節點ID,則說明連結串列有環,如果HashSet當中不存在相同的節點ID,就把這個新節點ID存入HashSet,之後進入下一節點,繼續重複剛才的操作。

這個方法在流程上和方法一類似,本質的區別是使用了HashSet作為額外的快取。

假設從連結串列頭節點到入環點的距離是D,連結串列的環長是S。而每一次HashSet查詢元素的時間複雜度是O(1), 所以總體的時間複雜度是1*(D+S)=D+S,可以簡單理解為O(N)。而演算法的空間複雜度還是D+S-1,可以簡單地理解成O(N)。

%e5%b0%8f%e4%bb%93%e9%bc%a0%e9%9d%a2%e8%af%95%e5%ae%984

%e5%b0%8f%e4%bb%93%e9%bc%a0%e8%8b%a6%e6%80%9d

%e5%b0%8f%e4%bb%93%e9%bc%a0%e8%8b%a6%e6%80%9d2

%e5%b0%8f%e4%bb%93%e9%bc%a0%e9%9d%a2%e8%af%95%e5%ae%980

%e5%b0%8f%e4%bb%93%e9%bc%a0%e5%a4%b1%e6%9c%9b

等通知就是沒通知,這是職場上公認的語言。

以上就是小灰悲劇的回憶……

%e5%b0%8f%e4%bb%93%e9%bc%a0%e8%ae%b2%e8%a7%a31

%e5%b0%8f%e4%bb%93%e9%bc%a0%e6%b2%89%e6%80%9d2

%e5%b0%8f%e4%bb%93%e9%bc%a0%e8%ae%b2%e8%a7%a32

方法三:首先建立兩個指標1和2(在java裡就是兩個物件引用),同時指向這個連結串列的頭節點。然後開始一個大迴圈,在迴圈體中,讓指標1每次向下移動一個節點,讓指標2每次向下移動兩個節點,然後比較兩個指標指向的節點是否相同。如果相同,則判斷出連結串列有環,如果不同,則繼續下一次迴圈。

例如連結串列A->B->C->D->B->C->D,兩個指標最初都指向節點A,進入第一輪迴圈,指標1移動到了節點B,指標2移動到了C。第二輪迴圈,指標1移動到了節點C,指標2移動到了節點B。第三輪迴圈,指標1移動到了節點D,指標2移動到了節點D,此時兩指標指向同一節點,判斷出連結串列有環。

此方法也可以用一個更生動的例子來形容:在一個環形跑道上,兩個運動員在同一地點起跑,一個運動員速度快,一個運動員速度慢。當兩人跑了一段時間,速度快的運動員必然會從速度慢的運動員身後再次追上並超過,原因很簡單,因為跑道是環形的。

%e8%b7%91%e9%81%93

假設從連結串列頭節點到入環點的距離是D,連結串列的環長是S。那麼迴圈會進行S*K次,K為正整數(為什麼是S*K次,有心的同學可以自己揣摩下),可以簡單理解為O(N)。除了兩個指標以外,沒有使用任何額外儲存空間,所以空間複雜度是O(1)。

%e5%b0%8f%e4%bb%93%e9%bc%a0%e8%ae%b2%e8%a7%a30

問題一:判斷兩個單向連結串列是否相交,如果相交,求出交點。

%e9%93%be%e8%a1%a8%e5%9b%be3

問題二:在一個有環連結串列中,如何找出連結串列的入環點?

%e9%93%be%e8%a1%a8%e5%9b%be2

 

 

 

 

 

 

%e5%b0%8f%e4%bb%93%e9%bc%a0%e5%be%97%e6%84%8f

 

本人微訊號:bjweimengshu

歡迎朋友們一起交流討論,加好友請註明伯樂線上 :)

打賞支援我寫出更多好文章,謝謝!

打賞作者

打賞支援我寫出更多好文章,謝謝!

漫畫演算法:如何判斷連結串列有環?

相關文章