求兩個鏈表是否相交並求出相交點


一、問題描述

有兩個鏈表,判斷是否相交並求出相交的點?

 

二、問題分析

大家看到題目會不由自主的想起一個很普遍的情況,就是下面

但是這個題目有一個陷阱就是,沒有講明兩個鏈表的結構,沒有很好地給出,其實有三種情況

(1)當兩個鏈表都無環,如上面

(2)當一個鏈表有環,另一個鏈表無環

(3)當兩個鏈表都有環

這三種情況,下面一一講解這些情況下兩個鏈表是否相交以及相交點。

 

三、問題解析

(一)當兩個鏈表都無環

 

3.1 方法1

鏈表相交后,發現后面的結點全部公用,我們可以這樣:從兩個鏈表的頭走到尾,判斷鏈尾地址信息是不是一樣,如果是則相交,反之則不交即可。

Node* LinkList::findByIndex(int index){  //根據索引返回節點信息
    Node* p = head;
    int i = 0;
    if (index<0||index >getLength()) {
        cout << "索引非法!" << endl;
        return NULL;
    }
    while (p) {
        if (i == index) 
            return p;
        else {
            p = p->next;
            i++;
        }
    }
    return NULL;
}

bool LinkList::isIntersect(LinkList preLinkList, LinkList forLinkList) {
    Node* tail1 = preLinkList.findByIndex(preLinkList.head->value);//鏈表1尾部
    Node* tail2 = forLinkList.findByIndex(forLinkList.head->value);//鏈表2尾部
    if (tail1 == tail2) {
        return true;
    }
    return false;
}

 

3.2 方法二

我們可以根據上圖,相交之后部分的長度是一樣的,相等的,所以我們可以這樣讓長鏈表的長度減去稍微較短的長度,得到兩個鏈表的相差長度,再讓長鏈表從頭結點開始遍歷這個長度,與此同時,短鏈表也向后走,若指針相等,鏈表就相交,反之,則不交

bool LinkList::isIntersect(LinkList preLinkList, LinkList forLinkList) {
    bool flag = false;
    if (preLinkList.head->value > forLinkList.head->value)
        flag = true;
    int length = abs(preLinkList.head->value - forLinkList.head->value);//相差的長度
    Node* p= preLinkList.head->next, *q= forLinkList.head->next;//此處初始化應對length=0的情況
    if (length) {
        if (flag) {//第一個鏈表長
            p = preLinkList.findByIndex(length)->next;
            q = forLinkList.head->next;
        }
        else {
            p = forLinkList.findByIndex(length)->next;
            q = preLinkList.head->next;
        }
    }
    while (p != q) { //若指針相等跳出  ,可能會同時為空
        p = p->next;
        q = q->next;
    }
    if (p)  //排序跳出時為空
        return true;
    else
        return false;
}

求相交點

Node* LinkList::findIntersectPoint(LinkList preLinkList, LinkList forLinkList){  //不帶環的鏈表求交點
    bool flag = false;
    if (preLinkList.head->value > forLinkList.head->value)
        flag = true;
    int length = abs(preLinkList.head->value - forLinkList.head->value);//相差的長度
    Node* p= preLinkList.head->next, *q= forLinkList.head->next;//此處初始化應對length=0的情況
    if (length) {
        if (flag) {//第一個鏈表長
            p = preLinkList.findByIndex(length)->next;
            q = forLinkList.head->next;
        }
        else {
            p = forLinkList.findByIndex(length)->next;
            q = preLinkList.head->next;
        }
    }
    while (p != q) { //若指針相等跳出  ,可能會同時為空
        p = p->next;
        q = q->next;
    }
    if (p)  //排序跳出時為空
        return p;
    else
        return NULL;
}

 

(二 )當一個鏈表不帶環,另一個帶環

如果一個鏈表不帶環,另一個鏈表帶環,兩個鏈表一定沒有相交點。因為相交之后,鏈表肯定有部分是共用,若有環,都會帶環,而且環的長度一樣,相交點也是環的入口處。

(3)兩個都帶環

就有兩種情況,一種交於環外,另外是交與環內,如下圖

                圖1

                圖2

通過上圖2發現,如果把環看成鏈表1的,則鏈表2相交於B點,反過來把環看出鏈表2的,則鏈表1相交於A處,所以你看的B處和A處,只是視覺上的,如果環是兩個鏈表的,則相交於環的任何結點上。

思路:

(1)因為環是大家共用的,我們先求出兩個鏈表的差L,也就是環外的差,並獲得鏈表的環入口點

(2)讓一個較長的鏈表從開始走過L個結點

(3)然后再讓短的鏈表從頭開始與之同步移動,保證兩個鏈表不走到環的入口時,判斷結點地址是否相同

(4)若出現相同而且未到入口,則返回交點,交於環外

(5)反之,直接返回鏈表的入口即可。

Node* LinkList::findIntersectPoint(LinkList preLinkList, LinkList forLinkList){  //帶環鏈表求交點
    int length1 = preLinkList.getCircleLinklistLength();
    int length2 = forLinkList.getCircleLinklistLength();   //獲得每個鏈表總長度
    Node* preEntry = preLinkList.findEnterCircle();
    Node* forEntry = forLinkList.findEnterCircle();       //找到每個的環入口點
    int length;
    Node* pre = preLinkList.head->next;
    Node* forth = forLinkList.head->next; //初始化各自鏈表的第一個節點的指針
    if (length1 > length2) {    //根據誰長誰短 決定誰后移指針
        length = length1 - length2;
        for (int i = 1;i <= length;pre = pre->next, i++);
    }
    else {
        length = length2 - length1;
        for (int i = 1;i <= length;forth = forth->next, i++);
    }
    while (pre != preEntry&&forth != forEntry&&pre != forth) { //向后移,注意是否走到環入口點
        pre = pre->next;
        forth = forth->next;
    }
    /*結束循環的3種情況
    1.=====2個鏈表任意一個或全部同時走到了環入口點,結束了循環
    2.======2個鏈表任意一個或全部同時走到了環入口點,並且pre=forth,結束了循環
    3.======沒有到達任何一個環入口點,pre=forth結束了循環
    */
    if (pre == forth) //對應情況2、3,如果是情況2,說明2個鏈表的環入口點相同;如果情況3,則說明在任何一個環入口之前找到了相交點
        return pre;
    else
        return preEntry;//對應情況1
}

 

以上就是鏈表的相交和相交點的思考,准備去BAT面試的大佬,還是可以讀一下的。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM