數字中重複的數字(不修改數組且不開闢新空間)

以前作過這樣的題,,以前的題是能夠修改數組的,那麼若是不能夠修改數組,咱們如何來作這個題呢?html

1.題目

給定一個長度爲 n+1 的數組nums,數組中全部的數均在 1∼n 的範圍內,其中 n≥1。c++

請找出數組中任意一個重複的數,但不能修改輸入的數組。數組

樣例指針

給定 nums = [2, 3, 5, 4, 3, 2, 6, 7]。

返回 2 或 3。

思考題:若是隻能使用 O(1) 的額外空間,該怎麼作呢?code

題目來自於這裏,有興趣的能夠作一下。htm

2.時間複雜度爲O(nlogn)的解法

本身沒想出來,看一位大佬寫的題解blog

2.1原理

該解法的原理就是抽屜原理與分治。索引

抽屜原理:n+1個蘋果放入到n個抽屜中,那麼一定有一個抽屜至少有兩個蘋果ip

這道題咱們能夠理解爲,有n+1個位置,咱們要給這n+1個位置賦值,範圍是1~n,那麼一定有一個數要有兩個位置。get

咱們能夠對這些數進行分治(不是位置)。在範圍1~n當中的數的個數加起來必定是n+1,一定大於n,咱們接下去縮小這個範圍,當在這個範圍中的數的個數加起來大於這個範圍的長度,就說明這個範圍中必定存在重複的數。依次下去,直到範圍的長度縮到1,咱們就找到了重複的數了。

2.2代碼

int duplicateInArray(vector<int>& nums) {
        int n = nums.size() - 1;
        int left = 1, right = n;
        while(left < right) {
        	int s = 0;
        	int mid = (left + right) / 2;
        	for(int i = 0; i <= n; i++) {
        		if(nums[i] >= left && nums[i] <= mid)
        			s++;
        	}
        	if(s > mid - left + 1)
        		right = mid;
        	else
        		left = mid + 1;
        }
        return right;
    }

3.時間複雜度爲O(n)的解法

題解原地址,看完只能說,大佬不愧是大佬,膜拜。

3.1原理

在這裏有一點前置的知識,談一下我看完他們後的見解吧

3.1.1 如何判斷一個鏈存在環

存在環,那麼咱們就有一個相遇的問題,讓一個快的指針和一個慢的指針去循環,它們就會相遇。

因此在這裏,咱們讓快指針每一次走兩步,慢指針每一次走一步,若是有環存在,那麼兩個指針必定會相遇。

3.1.2 快指針與慢指針相遇的地點以及起點的關係

借一下原地址大佬的圖

首先,快指針速度是慢指針的二倍,相同時間內,快指針的位移必定是慢指針的二倍。

慢指針在進入環後,走完一週以前,必定會與快指針相遇。

將上述兩個位移化簡得
b + (k - 2l)c = a
公式中k-2L必定是個整數,因此b + 整數倍的圈數 位置依然沒有變,咱們能夠讓k = 2L,這樣結果是不影響最終位置的。

要細討論的話是分爲三種狀況,與a 和 c的大小有關係。(此處就不討論了)

咱們即可以得出一個結論 a = 變化後的b 即相遇的地點與環起點相距b

3.1.3 將知識與題目結合

咱們能夠將上述知識應用到本題當中。咱們能夠將數組對應的數看成next值來處理,索引值看成值來處理,由抽屜原理得這個數組必定存在環。

咱們先從索引值爲0的數開始,將數組對應的值看成next,讓快指針移動兩格,慢指針移動一格,相遇的地點即P點,接下來咱們再讓快指針移動到0,而後一格一格的移動直到兩個指針相遇,此時索引值就是那個重複的數字。

3.2代碼

int duplicateInArray(vector<int>& nums) {
        int low = 0, fast = 0;
        while(low == 0 || low != fast) {
        	low = nums[low];
        	fast = nums[nums[fast]];
        }
        fast = 0;
        while(low != fast) {
        	low = nums[low];
        	fast = nums[fast];
        }
        return fast;
    }
相關文章
相關標籤/搜索