如何判斷單鏈表是否有環 環的入口 環的長度和總長

2021-08-22 19:28:59 字數 2813 閱讀 2982

問題描述

1.如何判斷單鏈表是否有環?

2.如果有環,求出環的入口

3.求環長

4.求總長

注意這裡長度:節點的數量

鍊錶定義參考:

** 要想判斷有環,我們可以聯絡實際生活中的例子,很容易就想到操場上跑圈,因為是環形,所以快的肯定會追上慢的,所以我們可以應用到鍊錶上,用乙個快指標和乙個慢指標,但是細想一下發現,我們在跑操的時候相遇時座標位置不一定是整數啊(這裡相比鍊錶節點而言的),而鍊錶是乙個節點連線起來,我們怎麼做,能讓他們在節點上相遇呢,這裡就要為2個指標找到合適的速度,使之能夠恰巧在某一結點上相遇。

原理:如果快的指標走到null,說明無環;而fast==slow相遇,則證明肯定存在環。

公式推導

為什麼存在環的情況下,兩個指標會相遇呢?以下推到n都是指 環長!

問題一

這裡寫描述

1.假定2個指標同乙個起點

我們讓兩個指標全部指向頭節點,然後給slow指標的速度為一步,而fast指標的速度為m步,則在第i次迭代的時候,slow指標走到i mod n,而fast指向mi mod n,要想slow和fast相遇,則i mod n=mi mod n,(m-1)i mod n,則我們可以令m=2(最小的可以取得值),i mod n = 0,則 i=n時,相遇,所以我們可以給fast 2倍的速度,這樣它們會在 最後乙個節點相遇。

2.假定不在同乙個起點,並且fast提前k位置

其實這個類似鍊錶中含有個小環的情況,即不是所有點在環中的情況,這樣當slow即將進入環狀的時候,fast已經在環中k mod n位置了,所以問題轉化為假定不在同乙個起點,並且fast提前k位置,是否會在一點相遇?

這裡寫描述

fast的速度仍設定為2倍,假定第i次迭代時,slow指向i mod n,fast指向k+2i mod n,其k大於0小於你,那麼i ≡ (2i+k)(mod n) -> (i+k) mod n = 0 -> 當i=n-k時,p與q相遇。

這裡寫描述

變相理解,如何同乙個起點出發,他們會在整圈(也就是最後乙個節點)相遇,現在fast在提前k位置出發,這樣就會使相遇點本來是最後節點,現在fast少走k步,即可與slow相遇,所以在n-k位置相遇。類似問題求倒數第k個節點:

所以不管是圖1的鍊錶,還是圖2的鍊錶,只要有環,快指標跟慢指標相遇,逆命題也成立;所有當快指標跟慢指標相遇,就一定存在環。

**:bool linklist::iscontaincirle()

} return !(fast==null || fast->next==null); //只要有乙個為空,就說明無環

}問題二

方法一

我們已經在上面的討論中,已經得知slow與fast會在環中n-k位置相遇,我們先靠主觀方面來**這個問題,兩個指標同時從頭節點開始走,當慢指標即將進入環中的時候,快指標位於k mod n,說明慢指標走的這段路程也能對應k mod n, 因為快指標是慢指標速度的2倍,所以快指標在環中走的距離與慢指標走的距離一樣。而我們發現相遇點位於n-k,再走k步就可以到達環的入口,並且慢指標走的路程也能對應k mod n,所以我們再令取2指標,乙個指向頭節點,另乙個指向碰撞點,都以1步的速度前進,這兩個指標相遇點就是環的入口,這個結論適用於全環的鍊錶,因為這時k=0,頭節點走一步就到了環的入口了。

以上只是我們主觀的理解方式,如果採用推導呢,slow走過的路程為s,環長為n,所以,2s=s+k+(m-1)n,化簡為s=k+(m-1)n,所以slow在環外相當於走了k+(m-1)n。

而碰撞點位於n-k的位置,所以要想走到環入點,則需要走k+mn步,這時你就會發現只要讓這兩個指標從頭節點與碰撞點以一步的速度過來,就一定會在環入點相遇,從而求出環入點!

**:node* linklist::findentercircle()

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

else

}方法二:

利用方法三求出的環長做,思路很簡單,思路就是既然我已經知道環長了,我完全可以使2個指標同時指向頭節點,然後令乙個指標走乙個環長的距離,再讓另乙個指標開始走,這樣兩個指標距離始終隔乙個環長,相遇的時候,就是環入口點。

思路來自這篇部落格:

**:node* linklist::findentercircle()

while (entercircle != q)

return entercircle;

}方法三

該方法需要先確定是否有環,否則結果無效。思路是用2個指標,其中乙個是另乙個的前驅,每次都把前驅的next指向null,即斷開,然後把後繼的指標賦值給前驅,後繼指標繼續後移,這樣當後繼指標為空時,前驅指標指的就是環入口點。因為環的入後的點在第一次進入環的時候斷開了,所以再次迴圈到這的時候,指標停住的地方就是環的入口。

此方法破壞了原有鍊錶的結構,不提倡這樣做,但是思路很好,我們在鍊錶定義的時候加入輔助標記變數,達到偽斷開的目的。

問題三

求環長就比較簡單了,儲存相遇點,然後令一指標從這出發向前,記錄走過的節點數,直到指標與相遇點相遇,即可求出環長。

**:int linklist::getcirclelength()

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

else

} return circlelength;

}問題四

總長度等於環外長度+環長度,環長度我們已求出,求環外長度,只需乙個指標從頭節點開始到環入口結束就行,記錄走過的節點數目。

**:int linklist::getcirclelinklistlength()

} return totallength;

}

鍊錶是否有環?環入口點?

1 如何判斷乙個鍊錶是不是這類鍊錶?2 如果鍊錶為存在環,如果找到環的入口點?解答 一 判斷鍊錶是否存在環,辦法為 設定兩個指標 fast,slow 初始值都指向頭,slow每次前進一步,fast每次前進二步,如果鍊錶存在環,則fast必定先進入環,而slow後進入環,兩個指標必定相遇。當然,fas...

判斷單鏈表是否有環及找環的入口

使用快慢指標,找到相遇節點 然後乙個指標指向頭節點,乙個指向相遇節點,一步步走直到兩個指標指向同乙個節點 即為環的入口點 include using namespace std typedef struct nodenode,linklist bool i istsloop linklist l r...

單鏈表判斷是否帶環,環的接入點

1 是否帶環判斷 使用追趕的方法,設定兩個指標slow fast,從頭指標開始,每次分別前進1步 2步。如存在環,則兩者相遇 如不存在環,fast遇到null退出。2 環的接入點 碰撞點p到連線點的距離 頭指標到連線點的距離,因此,分別從碰撞點 頭指標開始走,相遇的那個點就是連線點。判斷單鏈表是否帶...