以前作過這樣的題,,以前的題是能夠修改數組的,那麼若是不能夠修改數組,咱們如何來作這個題呢?html
給定一個長度爲 n+1 的數組nums
,數組中全部的數均在 1∼n 的範圍內,其中 n≥1。c++
請找出數組中任意一個重複的數,但不能修改輸入的數組。數組
樣例指針
給定 nums = [2, 3, 5, 4, 3, 2, 6, 7]。 返回 2 或 3。
思考題:若是隻能使用 O(1) 的額外空間,該怎麼作呢?code
題目來自於這裏,有興趣的能夠作一下。htm
本身沒想出來,看一位大佬寫的題解。blog
該解法的原理就是抽屜原理與分治。索引
抽屜原理:n+1個蘋果放入到n個抽屜中,那麼一定有一個抽屜至少有兩個蘋果ip
這道題咱們能夠理解爲,有n+1個位置,咱們要給這n+1個位置賦值,範圍是1~n,那麼一定有一個數要有兩個位置。get
咱們能夠對這些數進行分治(不是位置)。在範圍1~n當中的數的個數加起來必定是n+1,一定大於n,咱們接下去縮小這個範圍,當在這個範圍中的數的個數加起來大於這個範圍的長度,就說明這個範圍中必定存在重複的數。依次下去,直到範圍的長度縮到1,咱們就找到了重複的數了。
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; }
題解原地址,看完只能說,大佬不愧是大佬,膜拜。
在這裏有一點前置的知識,談一下我看完他們後的見解吧
存在環,那麼咱們就有一個相遇的問題,讓一個快的指針和一個慢的指針去循環,它們就會相遇。
因此在這裏,咱們讓快指針每一次走兩步,慢指針每一次走一步,若是有環存在,那麼兩個指針必定會相遇。
借一下原地址大佬的圖
首先,快指針速度是慢指針的二倍,相同時間內,快指針的位移必定是慢指針的二倍。
慢指針在進入環後,走完一週以前,必定會與快指針相遇。
將上述兩個位移化簡得
b + (k - 2l)c = a
公式中k-2L必定是個整數,因此b + 整數倍的圈數 位置依然沒有變,咱們能夠讓k = 2L,這樣結果是不影響最終位置的。
要細討論的話是分爲三種狀況,與a 和 c的大小有關係。(此處就不討論了)
咱們即可以得出一個結論 a = 變化後的b 即相遇的地點與環起點相距b
咱們能夠將上述知識應用到本題當中。咱們能夠將數組對應的數看成next值來處理,索引值看成值來處理,由抽屜原理得這個數組必定存在環。
咱們先從索引值爲0的數開始,將數組對應的值看成next,讓快指針移動兩格,慢指針移動一格,相遇的地點即P點,接下來咱們再讓快指針移動到0,而後一格一格的移動直到兩個指針相遇,此時索引值就是那個重複的數字。
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; }