首先看另一道題:AB兩條無環單鏈表,求A、B的第一個交點,沒有則返回null。
這道題可以根據加法交換律 來做,也就是分別用兩個指針p1、p2遍歷A和B,當到達鏈表末尾的時候,轉向另一條鏈表的頭結點,那麼如果換頭後p1和p2第一次相等了,就到達了A、B的第一個交點(或者null)。
爲什麼呢?因爲 ,所以當p1、p2都換頭之後,他們必定會同時到達鏈表末尾,那麼他們與各自所在鏈表末尾的距離就必然相等,那麼當他們第一次相等時,就是兩條鏈表的第一個公共交點。如果A、B長度相同,那麼在換頭前就會有滿足p1==p2的點。
假設鏈表的數據結構爲
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :val(x), next(NULL) {}
};
代碼可以寫成
ListNode* FindFirstCommonNode( ListNode* pHead1, ListNode* pHead2) {
auto p1=pHead1,p2=pHead2;
while(p1!=p2){
p1=p1?p1->next:pHead2;
p2=p2?p2->next:pHead1;
}
return p1;
}
回到我們的題目,首先可以用快慢指針來判斷有沒有環,如果有環,快慢指針就會在環內相遇(並且會在慢指針在環內轉一圈前相遇,因爲慢指針每運動一次,快指針到慢指針的距離就會-1),本算法不關心是環內的哪一點相遇,只要是環內的就行了。然後把原來的頭以及相遇節點的下一個節點分別作爲鏈表A、B的頭,把meet作爲A、B鏈表的末尾,如下圖。
根據上邊的代碼,當p1到達鏈表A的末尾即meet的時候,應該轉到鏈表B的頭,根據圖可知,meet->next就是就是B的頭,而p2到達meet的時候,應該轉向pHead1。所以代碼如下
ListNode* EntryNodeOfLoop(ListNode* pHead)
{
if(pHead==nullptr)return nullptr;
auto p1=pHead,p2=p1->next;
while(p2 && p2!=p1){
p2=p2->next;
if(p2==nullptr)return nullptr;
p2=p2->next;
p1=p1->next;
}
if(p2){
//有環
auto meet=p2;
p2=p2->next;
p1=pHead;
while(p1!=p2){
p2 = p2==meet?pHead:p2->next;
p1 = p1->next;
}
}
return p2;
}