單鏈表習題(進階一)

題目

  1. 判斷單鏈表是否帶環?若帶環,求環的長度?求環的入口點?並計算每個算法的時間複雜度&空間複雜度。
  2. 判斷兩個鏈表是否相交,若相交,求交點。(假設鏈表不帶環)
  3. 判斷兩個鏈表是否相交,若相交,求交點。(假設鏈表可能帶環)【升級版】

求帶環鏈表環長

(無環返回 -1)

思路

  1. 快慢指針法,快指針一次走兩步,慢指針一次走一步,如果鏈表帶環,則從慢指針到達入口點開始,由於快慢指針都在環上移動,每次移動快指針都追慢指針一步,慢指針走一圈之內,快指針一定追上慢指針。
  2. 相遇之後,一次追一步,再次相遇剛好追一圈——環的長度。

寫代碼

  1. 創建快慢指針等於頭指針,快指針及其下一節點不爲空(忽略慢指針),進入循環,快指針走兩步,慢指針走一步,如果快~等於慢~,break 結束循環。
  2. 判斷,如果快~不等於慢~或快指針下一節點爲NULL(如果鏈表只有一個節點,快慢指針都沒走,快~等於慢~),無環,返回NULL。
  3. 此時已確定鏈表帶環,創建計數器 count = 0,循環,計數,快慢指針再次相遇爲止。
  4. 返回 count。

代碼

int SizeOfRing(ListNode* pList)
{
    ListNode* fast = pList;
    ListNode* slow = pList;
    while (fast && fast->next)
    {
        fast = fast->next->next;
        slow = slow->next;
        if (fast == slow)
            break;
    }
    if (fast != slow || fast->next == NULL)
        return -1;
    else
    {
        int count = 0;
        do{
            fast = fast->next->next;
            slow = slow->next;
            count++;
        } while (fast != slow);
        return count;
    }
}

求環入口點

(無環返回 NULL)

思路一(快慢指針法)

  1. 同求環長,快慢指針相遇停止。
  2. 此時,將路程分爲三段,一是 T——尾長(非環長度),二是 S——慢指針入環到被追上,三是 C——快指針追慢指針多跑的圈。那麼,慢指針走的路程爲 T+S,快指針走的路程爲 T+S+C,可得 2(T+S)=T+S+C,T=C-S,此時,一個指針從起點出發走 T步,一個指針從快慢指針相遇點出發走 C-S 步,此時兩指針剛好都在入口點。
  3. 兩指針一個從頭結點出發,一個從快慢指針相遇點出發,一步一步走,相遇的節點即爲入口點(走的步數即爲尾長 T(C-S))。

寫代碼

  1. 判斷是否帶環並處理不帶環情況,同求環長。
  2. 兩個指針,一個從頭結點出發,一個從快慢指針相遇點出發,循環各走一步,相遇爲止,返回任意一個指針。

代碼

ListNode* IntranceOfRing(ListNode* pList)
{
    ListNode* fast = pList;
    ListNode* slow = pList;
    while (fast && fast->next)
    {
        fast = fast->next->next;
        slow = slow->next;
        if (fast == slow)
            break;
    }
    if (fast != slow || fast->next == NULL)
        return NULL;
    while (pList != slow)
    {
        pList = pList->next;
        slow = slow->next;
    }
    return pList;
}

思路二(轉化無環相交鏈表法)

(斷開完了記得接上)
1. 求快慢指針相遇點。
2. 保存相遇點下一節點 next,相遇點下一節點置空(斷環)。
3. 用原頭結點和 next 調用求無環鏈表相交節點函數(intersect1(),見下題),保存爲 ret。
4. 接環(相遇點下一節點賦 next),返回 ret。

相交鏈表求交點(無環)intersect1()

(無交點返回 NULL)

寫代碼

  1. 若無環鏈表相交,則尾節點相同(長這樣 >— ),新建指針遍歷兩鏈表,並統計步數(求交點用)。
  2. 尾節點不同,返回 NULL。
  3. 比較兩鏈表長度,快指針從長鏈表頭結點開始,走比短鏈表長的節點數。
  4. 慢指針從另一鏈表頭結點開始,與快指針一起一步一步走,指針相同時停止。返回任意一個指針。

代碼

ListNode* intersect1(ListNode* pList1, ListNode* pList2)
{
    int count1 = 0, count2 = 0;
    ListNode* p1 = pList1, *p2 = pList2;
    while (p1->next)
    {
        count1++;
        p1 = p1->next;
    }
    while (p2->next)
    {
        count2++;
        p2 = p2->next;
    }
    if (p1 != p2)return NULL;
    if (count1 >= count2)
    {
        count1 -= count2;
        while (count1--)
            pList1 = pList1->next;
        while (pList1 != pList2)
        {
            pList1 = pList1->next;
            pList2 = pList2->next;
        }
        return pList1;
    }
    else
    {
        count2 -= count1;
        while (count2--)
            pList2 = pList2->next;
        while (pList1 != pList2)
        {
            pList1 = pList1->next;
            pList2 = pList2->next;
        }
        return pList1;
    }
}

相交鏈表求交點(可能帶環)intersect2

(無交點返回 NULL, 環上相交返回第一個鏈表的環入口點)

思路

  1. 先列舉情況
    這裏寫圖片描述
  2. 求交點之前,需要先判斷是否帶環,用環入口點函數判斷,並保存入口點。
  3. 兩個入口點都爲空,是 1、2 兩種情況,直接使用無環鏈表求交點函數。
  4. 如果只有一個爲空,一定是第三種情況,不相交,返回 NULL。
  5. 剩下便是兩入口點都不爲空,且入口點相同,則是 3、4 兩種情況,不必做區分,統計從兩頭結點到入口點的長度,然後(同無環鏈表求交點3、4) 比較兩鏈表到入口點長度,快指針從長鏈表頭結點開始,走比短鏈表長的節點數。慢指針從另一鏈表頭結點開始,與快指針一起一步一步走,指針相同時停止。返回任意一個指針。
  6. 區分最後兩種情況通過一個指針從其中一個入口點出發,走一圈之內,如果與另一入口點相遇,是第 7 種情況,返回第一個鏈表的入口點(也可以是第二個),否則是第 6 種情況,返回 NULL。

代碼

ListNode* intersect2(ListNode* pList1, ListNode* pList2)
{
    ListNode* intr1 = IntranceOfRing(pList1);
    ListNode* intr2 = IntranceOfRing(pList2);
    if (intr1 == NULL && intr2 == NULL)
        return intersect1(pList1, pList2);
    else if (intr1 == NULL || intr2 == NULL)
        return NULL;
    else if (intr1 == intr2)
    {
        int count1 = 0, count2 = 0;
        ListNode* p1 = pList1, *p2 = pList2;
        while (p1->next != intr1)
        {
            count1++;
            p1 = p1->next;
        }
        while (p2->next != intr1)
        {
            count2++;
            p2 = p2->next;
        }
        if (count1 >= count2)
        {
            count1 -= count2;
            while (count1--)
                pList1 = pList1->next;
            while (pList1 != pList2)
            {
                pList1 = pList1->next;
                pList2 = pList2->next;
            }
            return pList1;
        }
        else
        {
            count2 -= count1;
            while (count2--)
                pList2 = pList2->next;
            while (pList1 != pList2)
            {
                pList1 = pList1->next;
                pList2 = pList2->next;
            }
            return pList1;
        }
    }
    else
    {
        ListNode* pList = intr2->next;
        while (pList != intr2 && pList != intr1)
        {
            pList = pList->next;
        }
        return pList == intr1 ? intr1 : NULL;
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章