題目:設計一個算法完成以下功能:判斷一個鏈表是否有環,如果有,找出環的入口點並返回。否則返回NULL
關鍵字:單鏈表+判斷循環
思路
關注:
有環的充要條件:兩個指針不停遍歷,一定會相遇
算法的基本設計思想:
1.設置快慢兩個指針分別爲fast和slow,初始時都指向鏈表頭head。
slow每次走一步,即slow=slow->next;
fast每次走兩步,即fast比slow走得快,如果有環,fast一定會先進入環,而slow後進入環。
當兩個指針都進入環後,經過若干次操作後兩個指針定能在環上相遇。
這樣就可以判斷一個鏈表是否有環。
如下圖所示,當slow剛進入環時,fast早已進入環。
因爲fast每次比slow多走一步且fast與slow的距離小於環的長度,所以fast與slow相遇時,slow所走的距離不超過環的長度。
如下圖所示,設頭結點到環的入口點的距離爲a, 環的入口點沿着環的方向到相遇點的距離爲x,環長爲r,相遇時fast繞過了n圈。(n=0,1,2,…)(上文已分析,slow走的距離一定等於X,總圈數一定小於等於一圈,不可能出現套圈情況)
相遇時:fast比slow多走的距離爲:n個環長
則有(a+x)/1=(a+nr+x)/2。整理得:a=nr-x
路程/速度=時間
Q:如何將通過數學得到的等式化爲算法呢?
A:回到指針遍歷的實際場景中找尋規律,最終目的是要求返回環的入口點地址。
因此,最理想情況就是當經過一番操作之後,p指針可以剛好降臨在環的入口點,此時返回p指針,就是最終結論。
而到達環的入口點,最常見的起點,即把鏈表第一個結點作爲起點而言,剛好需要行走過a個位置
而數學公式告訴我們:a=n*r-x
顯然:從頭結點到環的入口點的距離
等於
n倍的環長減去環的入口點到相遇點的距離
所以,對於兩個同步指針:
當一個完成從頭結點0~a
可以帶動
另一個完成從環入口點開始,移動n圈還差X個位置,最終到達(在環中)逆時針距離環入口x個位置的任務
即
讓另另一個完成從環入口點順時針後X個位置開始,完成n圈,最終剛好到達入口點的任務
因此可設置兩個指針,一個指向head,一個指向相遇點(即從環入口點順時針後X個位置),兩個指針同步移動(均爲一次走一步),相遇點即爲環的入口點。
LNode*FindLoopStart(LNode*head){
LNode *fast=head,*slow=head;//設置快慢兩個指針
while(slow!=NULL&&fast->next!=NULL){
slow=slow->next;//慢指針每次走一步
fast=fast->next->next;//快指針每次走兩步
if(slow==fast) break;//相遇
}
if(slow==NULL||fast->next==NULL)
return NULL;//沒有環,返回NULL
LNode*p1=head,*p2=slow;//確定有環開始找環的入口點。分別指向開始點、相遇點
while(p1!=p2){
p1=p1->next;
p2=p2->next;
}
return p1;//返回入口點
}