兩鏈表求公共節點

引言html

其實這道題目早在劍指Offer上就已經出現過,參見之前的這篇文章,但後來July前輩也有一片文章講過相似問題,對比之下,以前的解法還不夠全面,沒有考慮到全部狀況,這篇文章把這道題目做一個梳理。node

 

題目總結起來是這樣:ios

輸入兩個鏈表,判斷它們是否有公共節點,返回bool。ide

引伸:找出它們的第一個公共節點的指針,若是沒有公共節點則返回NULL。函數

鏈表的定義以下:測試

struct ListNode{
    int val;
    ListNode* next;
    ListNode(int x) : val(x), next(NULL) {};
};

 

輸出函數:spa

bool JudgeCommonListNode(ListNode* head1, ListNode* head2){
}


ListNode* FindFirstCommonListNode(ListNode* head1, ListNode* head2){
} //引伸,求第一個公共節點

 

 

首先須要考慮空指針輸入,有一個輸入爲NULL,就不會有交點。.net

尋找兩鏈表的第一個交點,簡便的作法是求出兩鏈表長度差diff後,讓長的鏈表先走diff步,而後依次比較兩鏈表節點,若相同則返回。指針

但這一個思路的前提是:兩個鏈表都不含環。code

所以,在考慮完空指針後,須要檢驗兩個鏈表是否含有環。

(1) 若一個有環一個無環,則確定沒有交點。

(2) 若都無環,能夠用上面的思路求出第一個交點;若是隻要求返回bool,則比較一下兩鏈表的尾節點是否相等便可。

(3) 兩鏈表都含有環的狀況下,若是隻須要返回bool值,咱們就檢驗其中一個鏈表的環入口是否在另外一個鏈表的環上,在環上就返回true,代表相交,不在環上返回false。

若是要返回第一個交點呢?這時候咱們能夠分狀況考慮:若是兩個鏈表都是「勺型」,所謂勺型,就是指不是純的環鏈表。那這兩個勺型鏈表若是有交點,環部分確定是已經百分百重疊了,第一個交點只能出如今「勺柄」上,既然是勺柄,天然就無環啦,所以,上面那個求兩個無環鏈表的第一個交點的方法有能夠拿來用了~

若是兩個鏈表都有環,並且至少有一個是純環鏈表,那麼若是有交點,這個純環鏈表必然全部節點都和另外一個鏈表的環部分重合。此時,咱們把那個純環鏈表的輸入節點(假設是head1)看做第一個交點,所以,只須要驗證一下head1是否在另外一個鏈表的環上便可。在的話就返回head1,不在就返回NULL。

 

所以這道題目其實並不簡單,它包含「判斷鏈表是否含有環」,「求環入口節點」,「求兩條無環鏈表的第一個交點」 三個子問題。

這裏給出引伸問題,也就是求第一個交點的題目的代碼,輔助函數traverse 的功能包含判斷是否有環,返回環入口節點(無環則返回NULL),以及求頭結點距離環入口節點(有環狀況)或者尾節點(無環狀況)的長度,這個長度被存在len變量裏。

在判斷是否有環的過程當中,須要注意單節點環鏈表的狀況。

ListNode* traverse(ListNode* head, int &len, bool &circled){
    ListNode* p1 = head -> next, *p2 = head -> next;
    int step = 1;
    for(; p2 != NULL; ++step, p1 = p1 -> next, p2 = p2 -> next){
        p2 = p2 -> next;
        ++step;
        if(!p2 || p1 == p2) break;
    }
    if(!p2){    //無環 
        len = step;    //用len記錄鏈表長度 
        circled = false;
        return NULL;
    }else{//有環 
        circled = true; 
        len = 0; //用len記錄頭結點到環入口距離 
        for(p1 = head; p1 != p2; p1 = p1 -> next, p2 = p2 -> next, ++len);
        return p1;
    }
}

ListNode* FindFirstCommonListNode(ListNode* head1, ListNode* head2){
    if(!head1 || !head2) return NULL;
    if(head1 == head2) return head1;
    int len1 = 0, len2 = 0; bool cc1 = false, cc2 = false;
    ListNode* CcleEnt1 = traverse(head1, len1, cc1);
    ListNode* CcleEnt2 = traverse(head2, len2, cc2);
    
    if((!cc1 && cc2) || (cc1 && !cc2)) return NULL;    //若一個有環,一個無環,則確定沒有交點
    if(len1 > 0 && len2 > 0){    //當兩鏈表都無環,或者都有環且首節點都不在環上時 
        ListNode *st1 = (len1 > len2 ? head1 : head2);
        ListNode *st2 = (len1 > len2 ? head2 : head1);
        ListNode *cce1 = (len1 > len2 ? CcleEnt1 : CcleEnt2);
        ListNode *cce2 = (len1 > len2 ? CcleEnt2 : CcleEnt1);
        for(int i = 0; i < (len1 - len2 > 0 ? len1 - len2 : len2 - len1); ++i, st1 = st1 -> next);
        for(; st1 != cce1 && st2 != cce2 && (st1 != st2); st1 = st1 -> next, st2 = st2 -> next);
        if(st1) return st1;
        return NULL;
    }else{    //len1, len2 中有一個爲0 說明其中至少有一條鏈表是純環鏈表。
        ListNode *st1 = (len1 == 0 ? head1 : head2); //選擇那個純環鏈表的head,驗證它是否在另外一個鏈表的環上,在的話它就是第一個交點,不在的話就沒有交點。 
        ListNode *st2 = (len1 == 0 ? head2 : head1);
        ListNode *p = st2 -> next;
        for(; p != st2 && p != st1; p = p -> next);
        if(p == st1) return st1;
        return NULL;
    }
}

 

 

帶測試用例的代碼:

#include <iostream>
using namespace std;

struct ListNode{
    int val;
    ListNode* next;
    ListNode(int x) : val(x), next(NULL) {};
};

ListNode* CreateListNode(int value)
{
    ListNode* pNode = new ListNode(value);

    return pNode;
}

void ConnectListNodes(ListNode* pCurrent, ListNode* pNext)
{
    if(pCurrent == NULL)
    {
        cout << "Error to connect two nodes.\n";
    }

    pCurrent -> next = pNext;
}

ListNode* traverse(ListNode* head, int &len, bool &circled){
    ListNode* p1 = head -> next, *p2 = head -> next;
    int step = 1;
    //cout << "---------\n";
    for(; p2 != NULL; ++step, p1 = p1 -> next, p2 = p2 -> next){    //只有一個節點的環鏈表 
        //cout << "p2: " << p2 -> val << endl;
        p2 = p2 -> next;
        ++step;
        if(!p2 || p1 == p2) break;
    }
    //cout << "---------\n";
    if(!p2){    //無環 
        len = step;    //用len記錄鏈表長度 
        circled = false;
        
        cout << "circled: " 
            << (circled ? "Yes" : "No") 
            << "; length: " << len << endl;
            
        return NULL;
    }else{//有環 
        circled = true; 
        len = 0; //用len記錄頭結點到環入口距離 
        for(p1 = head; p1 != p2; p1 = p1 -> next, p2 = p2 -> next, ++len);
        
        cout << "circled: " 
            << (circled ? "Yes" : "No") 
            << "; length: " << len << endl;
        
        return p1;
    }
}

ListNode* JudgeCommonListNode(ListNode* head1, ListNode* head2){
    if(!head1 || !head2) return NULL;
    if(head1 == head2) return head1;
    int len1 = 0, len2 = 0; bool cc1 = false, cc2 = false;
    ListNode* CcleEnt1 = traverse(head1, len1, cc1);
    ListNode* CcleEnt2 = traverse(head2, len2, cc2);
    
    if((!cc1 && cc2) || (cc1 && !cc2)) return NULL;    //若一個有環,一個無環,則確定沒有交點
    
    if(len1 > 0 && len2 > 0){    //當兩鏈表都無環,或者都有環且首節點都不在環上時 
        ListNode *st1 = (len1 > len2 ? head1 : head2);
        ListNode *st2 = (len1 > len2 ? head2 : head1);
        ListNode *cce1 = (len1 > len2 ? CcleEnt1 : CcleEnt2);
        ListNode *cce2 = (len1 > len2 ? CcleEnt2 : CcleEnt1);
        for(int i = 0; i < (len1 - len2 > 0 ? len1 - len2 : len2 - len1); ++i, st1 = st1 -> next);
        for(; st1 != cce1 && st2 != cce2 && (st1 != st2); st1 = st1 -> next, st2 = st2 -> next);
        if(st1) return st1;
        return NULL;
    }else{    //len1, len2 中有一個爲0 說明其中至少有一條鏈表是純環鏈表。
        ListNode *st1 = (len1 == 0 ? head1 : head2); //選擇那個純環鏈表的head,驗證它是否在另外一個鏈表的環上,在的話它就是第一個交點,不在的話就沒有交點。 
        ListNode *st2 = (len1 == 0 ? head2 : head1);
        ListNode *p = st2 -> next;
        for(; p != st2 && p != st1; p = p -> next);
        if(p == st1) return st1;
        return NULL;
    }
}


/**---------測試代碼----------*/
ListNode* CreateList1(){
    ListNode* pNode1 = CreateListNode(1);
    ListNode* pNode2 = CreateListNode(2);
    ListNode* pNode3 = CreateListNode(3);
    ListNode* pNode4 = CreateListNode(4);
    ListNode* pNode5 = CreateListNode(5);

    ConnectListNodes(pNode1, pNode2);
    ConnectListNodes(pNode2, pNode3);
    ConnectListNodes(pNode3, pNode4);
    ConnectListNodes(pNode4, pNode5);
    
    return pNode1;
}

ListNode* CreateCc1(){
    ListNode* pNode1 = CreateListNode(1);
    ListNode* pNode2 = CreateListNode(2);
    ListNode* pNode3 = CreateListNode(3);
    ListNode* pNode4 = CreateListNode(4);
    ListNode* pNode5 = CreateListNode(5);

    ConnectListNodes(pNode1, pNode2);
    ConnectListNodes(pNode2, pNode3);
    ConnectListNodes(pNode3, pNode4);
    ConnectListNodes(pNode4, pNode5);
    ConnectListNodes(pNode5, pNode2);
    
    return pNode1;
}

int main(){
//    ListNode* head1 = CreateList1();
//    ListNode* head2 = CreateCc1();
//    ListNode* res = JudgeCommonListNode(head1, head2);

//    ListNode* pNode1 = CreateListNode(1);
//    ListNode* pNode2 = CreateListNode(2);
//    ListNode* pNode3 = CreateListNode(3);
//    ConnectListNodes(pNode1, pNode2);
//    ConnectListNodes(pNode3, pNode2);
//    ListNode* res = JudgeCommonListNode(pNode1, pNode3);
//    
//    ListNode* pNode1 = CreateListNode(1);
//    ListNode* pNode2 = CreateListNode(2);
//    ConnectListNodes(pNode1, pNode2);
//    ListNode* res = JudgeCommonListNode(pNode1, pNode2);
//
//    ListNode* pNode1 = CreateListNode(1);
//    ListNode* pNode2 = CreateListNode(2);
//    ConnectListNodes(pNode1, pNode1);
//    ConnectListNodes(pNode2, pNode2);
//    ListNode* res = JudgeCommonListNode(pNode1, pNode2);

//    ListNode* pNode1 = CreateListNode(1);
//    ListNode* pNode2 = CreateListNode(2);
//    ListNode* pNode3 = CreateListNode(3);
//    ListNode* pNode4 = CreateListNode(4);
//    ListNode* pNode5 = CreateListNode(5);
//    ListNode* pNode6 = CreateListNode(5);
//    ConnectListNodes(pNode1, pNode2);
//    ConnectListNodes(pNode2, pNode3);
//    ConnectListNodes(pNode3, pNode4);
//    ConnectListNodes(pNode4, pNode5);
//    ConnectListNodes(pNode6, pNode3);

    ListNode* pNode1 = CreateListNode(1);
    ListNode* pNode2 = CreateListNode(2);
    ListNode* pNode3 = CreateListNode(3);
    ListNode* pNode4 = CreateListNode(4);
    ListNode* pNode5 = CreateListNode(5);
    ListNode* pNode6 = CreateListNode(6);
    ListNode* pNode7 = CreateListNode(7);
    ListNode* pNode8 = CreateListNode(8);
    ConnectListNodes(pNode1, pNode2);
    ConnectListNodes(pNode2, pNode3);
    ConnectListNodes(pNode3, pNode4);
    ConnectListNodes(pNode4, pNode5);
    ConnectListNodes(pNode5, pNode6);
    ConnectListNodes(pNode6, pNode3);
    ConnectListNodes(pNode7, pNode8);
    ConnectListNodes(pNode8, pNode2);
    //ListNode* res = JudgeCommonListNode(pNode1, pNode7);
    
    //ListNode* res = JudgeCommonListNode(pNode7, pNode3);
    ListNode* res = JudgeCommonListNode(pNode1, pNode5);
    
    if(res) cout << "First overlap is: " << res -> val << endl;
    else cout << "Not overlap" << endl;
    
    return 0;
} 
帶有Main函數的代碼
相關文章
相關標籤/搜索