鏈表找環方法註解

編程判斷兩個鏈表是否相交,原題假設兩個鏈表不帶環。

  問題本身的解答並不是本文的重點。

用指針p1、p2分別指向兩個鏈表頭,不斷後移;最後到達各自表尾時,若p1==p2,那麼兩個鏈表必相交
 
 擴展問題1:如果鏈表可能有環,上面的方法怎麼調整?
分情況討論:
如果兩個鏈表都沒有環,那麼同原算法;
如果兩個鏈表一個有環,一個沒環,那麼必然不相交。
(*)如果兩個鏈表都有環,判斷一個鏈表環上的任一點是否在另一個鏈表上,如果是,則必相交,反之不相交。這時,需要找到另一個鏈表完整的環都包括了哪些結點,才能進行判斷。(*)
可以看出,解答這個問題要解決判斷是否有環。

擴展問題2:如果必須要求出兩個鏈表相交的第一個節點呢?

(*)    思路:如果兩個尾結點是一樣的,說明它們有重合;否則兩個鏈表沒有公共的結點。
    在上面的思路中,順序遍歷兩個鏈表到尾結點的時候,我們不能保證在兩個鏈表上同時到達尾結點。這是因爲兩個鏈表不一定長度一樣。但如果假設一個鏈表比另一個長L個結點,我們先在長的鏈表上遍歷L個結點,之後再同步遍歷,這個時候我們就能保證同時到達最後一個結點了。由於兩個鏈表從第一個公共結點開始到鏈表的尾結點,這一部分是重合的。因此,它們肯定也是同時到達第一公共結點的。於是在遍歷中,第一個相同的結點就是第一個公共的結點。
    在這個思路中,我們先要分別遍歷兩個鏈表得到它們的長度,並求出兩個長度之差。在長的鏈表上先遍歷若干次之後,再同步遍歷兩個鏈表,直到找到相同的結點,或者一直到鏈表結束。PS:沒有處理一種特殊情況:就是一個是循環鏈表,而另一個也是,只是頭結點所在位置不一樣。 (*)
    對於特殊情況,相交的第一個結點可以是第一個鏈表的表頭,也可以是第二個鏈表的表頭。因爲從表頭開始二者就開始相交了。對於這種情況,如果判斷出二者都是循環鏈表,就可以直接返回其中之一的頭指針。
 


相關問題:求鏈表倒數第k個結點

(*)設置兩個指針p1,p2,首先p1和p2都指向head,然後p2向前走k步,這樣p1和p2之間就間隔k個節點,最後p1和p2同時向前移動,直至p2走到鏈表末尾。(*)
 
  現在來看,遺留問題是:1.如何判斷鏈表有環?2.如何找到鏈表環的入口?方法是有的,而且這個算法在3.11節程序改錯的擴展問題有所提示。簡單來說就是
 對於問題1,指針p1、p2指向表頭,每次循環p1指向後繼,p2指向後繼的後繼;循環的結束條件是,p2後繼爲空(無環)或p1==p2(有環)。
   這個方法網上沒有看到比較令我滿意的解釋,而《編程原本》(Elements of Programming)在第2章“變換及其軌道”中雖然有簡單的說明,可當時看的時候也不是特別理解(就算現在理解了,這本書上講得比較抽象,上面的說明放在這裏只能讓讀者越看越糊塗),可能是悟性不夠吧。一是不滿足於用舉例說明,二是覺得,如果是連續地移動,即像物體運動時位移和時間是連續的而不是這樣離散的,當然會相遇;而二者都是離散的,如果每次都發生p2剛好越過p1的情況呢?下面用易於理解的方式證明,這個解法中如果有環,p1和p2必同時在停留在某個節點。



  如左圖,在任意時刻,p1和p2都在環上。由於p1每次向前1步,p2每次向前兩步,用相對運動的觀點來看,把p1看作靜止,那麼p2每次相對p1向前1步,二者在順時針方向上的距離每經過一個時刻就減少1,直到變爲0,也即二者恰好相遇。這樣就證明了在離散情況下,對於有環鏈表,二者也是必然在某一時刻相遇在某個節點上的。


  沿着這個思路,繼續求解問題2尋找環的入口問題。記環的總長度爲R(即,環上有R個節點),並將上述的初始時刻選爲p1第一次到達環入口的時刻,相遇時p1在環上位置x%R,p2在環位置[2*(L+x)-L]%R,


  在相遇點,有(2L+2x-L)%R == x%R,即(L+2x)%R==x%R。根據同餘的性質,(L+x)%R==0。

  此時把p1放回表頭,p2的速度降爲1。當p1走了L到達環的入口時,p2在環上的位置爲(x+L)%R==0,這意味着p2回到了入口,且與p1相遇。並且由於之前p1不在環上,這是二者的在這一步操作後的第一次相遇,並且都在入口,這便找出了入口的位置。


  重述一遍尋找環存在和環入口的方法:


用兩個指針p1、p2指向表頭,每次循環時p1指向它的後繼,p2指向它後繼的後繼。若p2的後繼爲NULL,表明鏈表沒有環;否則有環且p1==p2時循環可以終止。此時爲了尋找環的入口,將p1重新指向表頭且仍然每次循環都指向後繼,p2每次也指向後繼。當p1與p2再次相等時,相等點就是環的入口。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章