1. 程式人生 > >《程式設計之美》——程式設計判斷兩個連結串列是否相交

《程式設計之美》——程式設計判斷兩個連結串列是否相交

問題:
給出兩個連結串列的頭指標,判斷這兩個連結串列是否相交。假設兩個連結串列均不帶環。
這裡寫圖片描述

分析與解法:

【解法一】
如果兩個連結串列都無環,則可以把第二個連結串列接在第一個連結串列後面,如果得到的連結串列有環,則說明這兩個連結串列相交。這裡如果有環,則第二個連結串列的表頭一定在環上,只需要從第二個連結串列開始遍歷,看是否會回到起點即可判斷。假設兩個連結串列長度分別為m和n,則時間複雜度為O(m+n)。
這裡寫圖片描述

【解法二】
若兩個連結串列都無環且交於一點,那麼最後一個節點一定是共有的。可以先遍歷第一個連結串列,記錄最後一個節點,再遍歷第二個連結串列,將其最後一個節點與第一個連結串列的最後一個節點比較,若相同,則相交。時間複雜度也為O(m+n)。

擴充套件問題:

  1. 若連結串列可能有環該如何求解。
  2. 求兩個連結串列相交的第一個節點。

【問題1】
首先要判斷有頭指標的單鏈表是否有環。
解法:使用追逐的方法,設定兩個指標slow、fast,從頭指標開始,每次分別前進1步、2步。如果存在環,則兩者相遇;如果不存在環,fast遇到NULL退出。

bool isExistLoop(LinkList *head)
{
    LinkList *slow = head;//slow指向頭指標
    LinkList *fast = head;//fast指向頭指標

    while(fast && fast -> next)//排除連結串列有單個節點或為空時的情況
{ slow = slow -> next; fast = fast -> next -> next; if(slow == fast) break; } return !(fast == NULL || fast -> next == NULL}); }
  • 若兩個都無環,回到原始問題;
  • 若一個有環,一個無環,不用判斷了,肯定兩連結串列不相交;
  • 若兩個都有環,判斷第一個連結串列的碰撞點(fast與slow相遇的節點)是否出現在第二個連結串列的環中,如果在,則相交。(相交時,環必定是兩連結串列共有的。)

【問題2】
同樣,使用追逐辦法先判斷是否存在環,分情況討論。

  • 若無環,人為構造,將第一個連結串列的尾節點指向第二個連結串列,則構成一個帶環的單鏈表。這個問題就轉換成尋找帶環單鏈表的環入口節點

    尋找帶環單鏈表的環入口節點的方法:
    碰撞點到交點的距離=頭指標到交點的距離,因此,分別從碰撞點、頭指標開始走,相遇的那個點就是交點。

LinkList findLoopPort(LinkList *head)
{
    LinkList *slow = head;
    LinkList *fast = head;

    while(fast && fast -> next)
    {
        slow = slow -> next;
        fast = fast -> next -> next;
        if(slow == fast)
            break;
    }

    if(fast == NULL || fast -> next == NULL})
        return NULL;

    slow = head;//slow指向頭指標
    while(slow != fast)//此時fast指向碰撞點
    {
        slow = slow -> next;
        fast = fast -> next;
    }
    return slow;
}
  • 若有環,計算出兩連結串列的長度len1、len2(環的長度與環的頭指標到交點長度之和就是連結串列長度)。
    如果len1>len2,則第一個連結串列的指標先走len1-len2,然後第二個連結串列的指標開始走,兩者相遇的點就是相交點;
    如果len2>len1,則第二個連結串列的指標先走len2-len1,然後第一個連結串列的指標開始走,兩者相遇的點就是相交點。

    求環的長度的方法:
    讓slow、fast從碰撞點開始走,再次碰撞所走過的運算元就是環的長度。

int loopLength(LinkList *head)
{
    LinkList *slow = head, *fast = head;
    LinkList *p;
    int length = 0;

    while(fast && fast -> next)
    {
        slow = slow -> next;
        fast = fast -> next -> next;
        if(slow == fast)
            break;
    }

    if(fast == NULL || fast -> next == NULL})
        return 0;

    while(true)
    {
        slow = slow -> next;
        fast = fast -> next -> next;
        length++;
        if(slow == fast)
            break;
    }
    return length;
}