Leetcode題解:287. Find the Duplicate Number尋找重複數

代碼實現

只想抄個答案把題過了的可以直接複製下面的代碼:

class Solution {
public:
	int findDuplicate(vector<int>& nums) {
		int slow = 0, fast = 0, tar = 0;

		do {
			slow = nums[slow];
			fast = nums[nums[fast]];
		} while (slow != fast);

		while (tar != slow) {
			slow = nums[slow];
			tar = nums[tar];
		}
		return tar;
	}
};

題目描述

描述

算法分析

由於題中的限制,所以不能用先排序再遍歷的方法(不能更改原數組),也不能用無序集合或者散列表來做(只能使用常量空間),所以只能從數組本身找規律,這裏介紹一種線性時間複雜度的算法。
首先我們有如下的一個數組,i爲下標,num爲數字,有nums[i] = num(nums爲給定的數組):
數組
然後我們建立這樣的映射F,使得F(i) = num
同時,如果給出了F(a) = b,我們再對b進行映射,會有F(b) = c,我們再對c做同樣的操作,一直不斷循環,反映到題中的數組上,就是如下的情況:
映射
這裏我們起始給定a = 0,F(a) = 3,然後F(3) = 2,F(2) = 1,…,F(4) = 2,F(2) = 1,出現了循環,但是並不是因爲有重複數字纔出現環,我們先看重複數字會發生什麼樣的情況:

會出現多對一映射,即至少存在這樣的a,b,c(a ≠ b),有F(a) = c,F(b) = c,反應到圖中即爲指向數字‘2’的有兩個箭頭(F(3) = 2,F(4) = 2)

很容易知道,如果我們對一個不存在重複數字的數組進行這樣的循環映射操作,最後也會發生循環,且循環開始的下標值一定是第一個進行映射操作的下標值,即,如果我們從F(a) = b開始進行映射操作,最後仍會以F(a) = b的出現標誌着進入循環(可以自己畫一個簡單的圖來驗證)。
但是如果有重複的數字,我們就會發現,因爲存在F(a) = c,F(b) = c,那麼中間步驟一旦出現了F(x) = b,就會有這樣的循環:F(c) = y, ..., F(x) = b, F(b) = c, F(c) = y,這種循環和剛剛講的循環不同的地方在於,映射循環開始的下標值正好是發生重複的數字,所以問題就轉化爲如何找到這樣的循環,以及循環開始的節點

代碼分析

首先,接下來的操作僅作說明,如果想了解原理可以參考鏈表找環的起點
算法一共就兩個要點,第一,找到環第二,找到環起點
先來看找環操作,如下:

do {
	slow = nums[slow];
	fast = nums[nums[fast]];
} while (slow != fast);

通過一快一慢兩個指針進行循環映射操作(慢指針一次走一格,快指針一次走兩格),兩個指針相遇時,就說明指針已經進入環內(前提是必須有環)。
再看找環起點的操作,如下:

while (tar != slow) {
	slow = nums[slow];
	tar = nums[tar];
}
return tar;

slow是剛纔循環結束後的慢指針,tar初始值是0,兩個指針同時向前走,相遇的時候tar指針就是環的起點,即數組重複的數字。
整個流程如下:

int findDuplicate(vector<int>& nums) {
	int slow = 0, fast = 0, tar = 0;
	
	do {
		slow = nums[slow];
		fast = nums[nums[fast]];
	} while (slow != fast);
	
	while (tar != slow) {
		slow = nums[slow];
		tar = nums[tar];
	}
	return tar;
}

最後,這個代碼雖然在leetcode上可以通過,但是還是有一個bug的,假如輸入{2,1,0,3,3}這樣的數組你會發現輸出結果是0,因爲0,1,2這3個數組成了環,相當於我們一開始說的無重複數字的數組產生的環,這時候就需要額外的進行判斷,有興趣的可以進行嘗試改進(提示:觀察這樣的數組輸出結果的規律,以及其中重複數字出現位置的規律

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章