1. 程式人生 > >快慢指標在連結串列中的一些證明

快慢指標在連結串列中的一些證明

目錄

一、一定會相遇的證明

二、環長度

三、連線點

四、帶環連結串列總長度

五、例題


一、一定會相遇的證明

1、如果連結串列沒有環,那麼快指標比慢指標先到達尾部(null)

2、如果連結串列有環的話,因為快指標走的比慢指標快,所以在環中相遇的過程可以看作是快指標從環後邊追趕慢指標的過程。

用遞迴法證明,快慢指標一定會相遇:

(1)快指標與慢指標之間差一步。此時繼續往後走,慢指標前進一步,快指標前進兩步,兩者相遇。
(2)快指標與慢指標之間差兩步。此時繼續往後走,慢指標前進一步,快指標前進兩步,兩者之間相差一步,轉化為第一種情況。
(3)快指標與慢指標之間差N步。此時繼續往後走,慢指標前進一步,快指標前進兩步,兩者之間相差(N+1-2)即N-1步。重複這個過程,直到快指標和慢指標相遇。

因此,此題得證。所以快指標必然與慢指標相遇。

 

推導:慢指標進入環後,快指標最多多繞一個圈。

快指標F先進環,慢指標S後進。

假設慢指標進環那一刻快指標差m步能追上(0<= m < Length環),根據上邊結論,兩個指標走m次就會相遇了。

因為m < Length環,所以快指標在慢指標進環那一刻最多比慢指標多繞一個圈。

 

二、環長度

快指標和慢指標第一次相遇時的節點pq(碰撞點),快指標和慢指標從該點開始繼續往前走,再次碰撞時所用的運算元就是環的長度Length環。

證明:由上邊的推導可得,這裡的m為Lengh環。

 

三、連線點

 

假設慢指標進入環中時,即連線點p,快指標(q)需要m步才能追上慢指標。

p和q第一次相遇時,碰撞點在pq處。此時,p走到pq時用了m步。

 假設head到p的距離為a,環長度為Length環,慢指標走了s步,則快指標走了2s步。

從上圖可知:

s = a + m

2s = a + m + n * Length環(n為快指標繞環的圈數)

可得

a = n * Length環 - m

也就是:若在頭結點和相遇結點分別設一指標,同步(單步)前進,則最後一定相遇在環入口結點p。

可根據這個結論來找到入口節點。

四、帶環連結串列總長度

找到連線點p後,求head到p的長度,再加上環的長度,即為連結串列的總長。

 

五、例題

給定一個包含 n + 1 個整數的陣列 nums,其數字都在 1 到 之間(包括 1 和 n),可知至少存在一個重複的整數。假設只有一個重複的整數,找出這個重複的數。

示例 1:

輸入: [1,3,4,2,2]
輸出: 2

示例 2:

輸入: [3,1,3,4,2]
輸出: 3

說明:

  1. 不能更改原陣列(假設陣列是隻讀的)。
  2. 只能使用額外的 O(1) 的空間。
  3. 時間複雜度小於 O(n2) 。
  4. 陣列中只有一個重複的數字,但它可能不止重複出現一次

【程式碼】

class Solution {
public:
    int findDuplicate(vector<int> &nums) {
        int s = nums[0];
        int f = nums[nums[0]];
        while(s != f){
            s = nums[s];
            f = nums[nums[f]];
        }
        f = 0;
        while (f != s)
        {
            f = nums[f];
            s = nums[s];
        }
        return f;
    }
};