今天分享的題目來源於 LeetCode 第 287 號問題:尋找重複數。算法
給定一個包含 n + 1 個整數的數組 nums,其數字都在 1 到 n 之間(包括 1 和 n),可知至少存在一個重複的整數。假設只有一個重複的整數,找出這個重複的數。編程
示例 1:數組
輸入: [1,3,4,2,2]
輸出: 2複製代碼
示例 2:spa
輸入: [3,1,3,4,2]
輸出: 3複製代碼
說明:指針
我的博客:www.cxyxiaowu.comcode
給定一個整形數組,數組的長度是 n + 1,數組裏面存放的元素是區間 [1, n] 上的數,這個數組中有且僅有一個元素是重複的,題目讓咱們找出這個元素,而且這道題給了以下的限制:排序
首先不能改變數組致使沒法排序,也沒法用 index 和元素創建關係;博客
只能使用 O(1) 的空間意味着使用哈希表去計數這條路也走不通;io
時間複雜度必須小於 O(n^2) 表示暴力求解也不行;ast
重複的元素可重複屢次 這一條加上後,原本能夠經過累加求和而後作差 sum(array) - sum(1,2,...,n)
的方式也變得不可行。
原本是很是簡單的一道數組遍歷的題目,加上了上面這四個條件後感受有點無從下手。
咱們說作題要藉助算法,而不是空想,所以對於這道題也不例外,咱們能夠問本身這樣一個問題,就是 「什麼樣的算法能夠不使用額外的空間解決數組上面的搜索問題?」。
靜靜的思索一下。
你這時應該隱隱約約知道了,難道是 二分查找!
什麼?二分查找法?!
二分查找法不是對有序數組才適用麼?
這裏澄清一個誤區,二分法的使用 並不必定 須要在排序好的數組上面進行,不要讓常見的例題限制了你的思路,二分法還有一個比較高級的用法叫作 按值二分。
這道題目交代的信息不多,咱們只須要關注兩個東西 - 數組,數組裏的元素,利用二分咱們須要去思考的是,咱們要找符合條件的元素做爲答案,那麼比答案小的元素具備什麼樣的特質,比答案大的元素又具備什麼樣的特質?,結合題目給咱們的例子來看看:
( 說明:下面的 <= 符號代表="" 小於或者等於。)
例1:
[1,3,4,2,2] 元素個數
<= 1 的元素:1 1
<= 2 的元素:1, 2, 2 3
<= 3 的元素:1, 2, 2, 3 4
<= 4 的元素:1, 2, 2, 3, 4 5複製代碼
例2:
[3,1,3,4,2]
<= 1 的元素:1 1
<= 2 的元素:1, 2 2
<= 3 的元素:1, 2, 3, 3 4
<= 4 的元素:1, 2, 3, 3, 4 5複製代碼
極端一點的例子 (必須保證數組的長度是 n + 1, 而且元素都在區間[1,n] 上, 有且只有一個重複)
[3,3,3,3,4]
<= 1 的元素: 0
<= 2 的元素: 0
<= 3 的元素:3, 3, 3, 3 4
<= 4 的元素:3, 3, 3, 3, 4 5複製代碼
看完上面幾個例子,相信你明白了一個事實:
並且你能夠看到,咱們要找的答案其實就處於一個分界點的位置,尋找邊界值,這又是二分的一個應用,並且題目已經告訴咱們數組裏面的值只可能在 [1, n] 之間,這麼一來,思路就是在 [1, n] 區間上作二分,而後按咱們以前提到的邏輯去作分割。整個解法的時間複雜度是 **O(nlogn)**,也是知足題目要求的。
上面的解法不是最優的,可是我的以爲是根據現有的知識比較容易想到的。
另一種 O(n) 的解法借鑑快慢指針找交點的思想,算法很是的巧妙,也很是的有趣,但不太容易想到,這裏把代碼放上。
//二分查找
class Solution {
public int findDuplicate(int[] nums) {
int len = nums.length;
int start = 1;
int end = len - 1;
while (start < end) {
int mid = start + (end - start) / 2;
int counter = 0;
for (int num:nums) {
if (num <= mid) {
counter++;
}
}
if (counter > mid) {
end = mid;
} else {
start = mid + 1;
}
}
return start;
}
}複製代碼
//快慢指針
public int findDuplicate(int[] nums) {
int fast = nums[nums[0]];
int slow = nums[0];
while (fast != slow) {
fast = nums[nums[fast]];
slow = nums[slow];
}
slow = 0;
while (fast != slow) {
fast = nums[fast];
slow = nums[slow];
}
return slow;
}複製代碼
若是你以爲這篇內容對你挺有啓發,我想邀請你幫我三個忙: