判斷乙個鍊錶是否有環,空間複雜度是o(1)
如果不考慮空間複雜度,可以使用乙個map記錄走過的節點,當遇到第乙個在map中存在的節點時,就說明回到了出發點,即鍊錶有環,同時也找到了環的入口。
不適用額外記憶體空間的技巧是使用快慢指標,即採用兩個指標walker和runner,walker每次移動一步而runner每次移動兩步。當walker和runner第一次相遇時,證明鍊錶有環
以為例,假設環的長度為r
,當慢指標walker
走到環入口時快指標runner
的位置如圖,且二者之間的距離為s
。在慢指標進入環後的t
時間內,快指標從距離環入口s
處走了2t
個節點,相當於從環入口走了s+2t
個節點。而此時慢指標從環入口走了t個節點。
假設快慢指標一定可以相遇,那麼有s+2t−t=nr
,即s+t=nr
,如果對於任意的s,r,n
,總可以找到乙個t
滿足上式,那麼就可以說明快慢指標一定可以相遇,滿足假設(顯然可以找到)
而實際上,由於s所以如果鍊錶中有環,那麼當慢指標進入到環時,在未來的某一時刻,快慢指標一定可以相遇,通過這個也就可以判斷鍊錶是否有環
**如下
如果鍊錶有環,尋找環入口位置
以為例,假設環入口距離煉表頭的長度為l,快慢指標相遇的位置為cross
,且該位置距離環入口的長度為s。考慮快慢指標移動的距離,慢指標走了l+s
,快指標走了l+s+nr
(這是假設相遇之前快指標已經繞環n圈)。由於快指標的速度是慢指標的兩倍,相同時間下快指標走過的路程就是慢指標的兩倍,所以有2(l+s)=l+s+nr
,化簡得l+s=nr
當n=1
時,即快指標在相遇之前多走了一圈,即l+s=r
,也就是l=r−s
,觀察,l表示從煉表頭到環入口的距離,而r−
s表示從cross
繼續移動到環入口的距離,既然二者是相等的,那麼如果採用兩個指標,乙個從表頭出發,乙個從cross
出發,那麼它們將同時到達環入口。即二者相等時便是環入口節點
當n>1
時,上式為l=nr−s
,ll仍然表示從煉表頭到達環入口的距離,而nr−s
可以看成從cross
出發移動nr
步後再倒退ss步,從cross
移動nrnr步後回到cross
位置,倒退s步後是環入口,所以也是同時到達環入口。即二者相等時便是環入口節點
所以尋找環入口的方法就是採用兩個指標,乙個從表頭出發,乙個從相遇點出發,一次都只移動一步,當二者相等時便是環入口的位置
**如下
/**
* definition for singly-linked list.
* struct listnode
* };
*/class solution
if(!runner || !runner->next)
return nullptr;
auto headwalker = head;
auto crosswalker = walker;
while(headwalker != crosswalker)
return headwalker;}};
其它的和環有關的題目記得還有
第一種方法是利用上面求出的環入口,再走一圈就可以求出長度,**如下
int cyclelen(listnode* head)
return len;
}
第二種方法是當快慢指標相遇時,繼續移動直到第二次相遇,此時快指標移動的距離正好比慢指標多一圈,**如下
int cyclelen(listnode* head)
int len = 0;
while(runner && runner->next)
return len;
}
from: 如何判斷乙個鍊錶是否有環,如果有環,並找出環的入口
首先如何判斷乙個鍊錶是否有環 設定兩個指標 fast,slow 初始值都指向頭,slow 每次前進一步,fast 每次前進二步,如果鍊錶存在環,則 fast 必定先進入環,而 slow 後進入環,兩個指標必定相遇。當然,fast 先行頭到尾部為 null 則為無環鏈表 程式如下 bool i its...
判斷乙個單鏈表是否有環,如果有,找出環的起始位置
第一種方法是從單鏈表head開始,每遍歷乙個,就把那個node放在hashset裡,走到下乙個的時候,把該node放在hashset裡查詢,如果有相同的,就表示有環,如果走到單鏈表最後乙個node,在hashset裡都沒有重複的node,就表示沒有環。這種方法需要o n 的空間和時間。第二種方法是設...
如何判斷鍊錶是否有環 鍊錶是否有環的判斷
對於鍊錶是否存在環,有三個問題需要考慮 1.是否有環 2.入環節點 3.環的長度 第一種方法快慢指標法,也稱之為龜兔演算法,設定兩個指標,慢指標和快指標。最開始均指向鍊錶的頭節點,之後,快指標每次後移兩個節點,慢指標每次後移乙個節點。1.如果快指標指向空,則鍊錶無環 2.若快指標和慢指標再次指向乙個...