找出鏈表中環的入口節點


前言

  今天刷《劍指offer》的編程題,遇見一道挺有意思的題目,叫鏈表中環的入口節點,寫篇博客記錄一下。


描述

  給出一個鏈表,在這個鏈表中至多存在一個環,要求:若鏈表中有環,則返回環的入口節點,若沒有環,返回null


思路

  我們可以設置兩個指針求解此問題:一個快指針fast,每次向前走兩個節點,一個慢指針low,每次向前走一個節點;我們首先需要知道一個結論:這兩個指針一快一慢向前走,若鏈表有環,則兩個指針一定會相遇

  這里先明確三個名稱:

  • 頭節點:表示鏈表的第一個節點;
  • 環入口節點:若鏈表中有環,則進入這個環的第一個節點;
  • 相遇點:快慢指針相遇的節點;

  我們設置三個變量來求解這個問題:

  • 變量a:頭節點環入口節點的距離;
  • 變量b:從環入口節點 沿鏈表方向前進到 相遇點的距離;
  • 變臉c:從相遇點 沿鏈表方向到 環入口節點的距離;

  所以,鏈表的長度 = a + b + c,而環的長度就是b+c

  畫個圖,把上面說的變量和名稱都標記上,我們不難看出,從開始到兩個指針相遇,慢指針low走過的距離為a+b,而快指針fast走過的距離為a + k×(b+c) + b,其中k是一個整數,且k>0(因為如果k==0,那快慢指針走過的距離就相等了);快指針fast每次前進兩個節點,而慢指針low每次前進一個節點,所以我們可以得到一個結論:相同時間內,快指針的走的路程是慢指針的兩倍

  根據以上結論,我們可以得到一個公式:2×(a+b) == a + k × (b+c) + b,而這個公式可以進行化簡,過程如下:

2×(a+b) == a + k × (b+c) + b;
2a + 2b == a + k × (b+c) + b;
a + b == k × (b+c);
a == k × (b+c) - b;
a == (k-1) × (b+c) + c;  // 最終結果

  經過一系列化簡,我們可以得到a == (k-1) × (b+c) + c,而上面我們說過,(b+c)就是鏈表中環的長度,所以這個公式可以解釋為:從鏈表頭節點到環入口節點的距離,等於環長度的k-1倍,再加上從相遇點到環入口節點的距離

  上面的解釋說明了什么?說明:兩個指針,一個從鏈表頭節點沿着鏈表向前走,另一個從快慢指針相遇點沿着鏈表向前走,最后它們會在環入口節點相遇(這里要好好理解一下);我們可以想象一下,指針在相遇點時,根據上面的最終結果,要向前走(k-1) × (b+c) + c這么長,而我們又知道,(b+c)就是環的長度,走一個b+c,就是饒了一圈,又回到了相遇點,所以當走完(k-1) × (b+c),其實又回到了相遇點;而這時,我們還要再向前走c個單位,c我們在上面已經說過了,就是相遇點到環入口的距離,所以走完c,指針就到了環入口了;而a我們也說過了,它是頭節點走到環入口的距離。所以,才有了上面的說明。

  所以,這道題目的求解步驟就出來了:設置一個快指針fast,每次向前走兩個單位,設置一個慢指針low,每次向前走一個單位,當這兩個指針相遇,到達相遇點時,我們將設立一個指針指向鏈表頭節點,另一個指針指向相遇點,兩個指針速度均為1,沿着鏈表前進,最后它們相遇的位置,就是環入口節


代碼

public ListNode EntryNodeOfLoop(ListNode pHead) {
    if(pHead == null) {
        return null;
    }
    // 設置快慢指針,快指針一次走兩步,慢指針一次走一步
    ListNode fast = pHead;
    ListNode low = pHead;
    do {
        // 快指針先走一步
        fast = fast.next;
        // 若fast為null,表示沒環,直接return空
        if(fast == null) {
            return null;
        }
        // 若不為null,再向前走一步
        fast = fast.next;
        // 慢指針向前走一步
        low = low.next;
    }while(fast != null && fast != low);

    // low指針指向鏈表頭節點,fast指針不變,還是在相遇點
    // 兩個指針速度均為1,向前走,再次相遇的點就是環入口節點
    low = pHead;
    while(low != fast) {
        low = low.next;
        fast = fast.next;
    }
    return low;
}


免責聲明!

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



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