不推公式不用證明不斷鏈,求單鏈表中環的入口結點

首先看另一道題:AB兩條無環單鏈表,求A、B的第一個交點,沒有則返回null。

這道題可以根據加法交換律 a+b=b+a  來做,也就是分別用兩個指針p1、p2遍歷A和B,當到達鏈表末尾的時候,轉向另一條鏈表的頭結點,那麼如果換頭後p1和p2第一次相等了,就到達了A、B的第一個交點(或者null)。

爲什麼呢?因爲 a+b=b+a ,所以當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;
    }

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章