FindDuplicateNumber
- 問題描述:給定一個數組長度爲N,裏面元素的取值範圍是1~N-1。所有會有一個數是重複的,請找到這個重複的數字並返回。
- 解法1:暴力
- 最直接的辦法就是利用兩重循環來尋找
- 當碰到相同的數字的時候就返回
- 時間複雜度是O(N)
- 解法2: 二分法
- 我們知道我們N個數是分佈在1~N-1之間的。所有假設我們統計1~N/2, N/2-N-1之間的數字,如果哪一方的數字多餘一半,則重複的數字就在其中。
- 通過這樣的方式我們可以二分的查找多餘的數字
- 時間複雜度是O(NlogN)
- 解法3:
- 我們嘗試構造下面的數列:
- X0=nums[0]
- X1=nums[X0]
- X2=nums[X1]
- …
- Xi=nums[Xi−1]
- 因爲我們數字裏存在重複的數字,所有一定存在一個Xi在[X0,Xi−1]之中出現過。形成如下所示的帶環鏈表。
- 接下來我們就是要找到入口前的一個點,他就是我們重複的數字。
- 那麼我們如何找呢?
- 我們先利用兩個point,一快一慢,快的走兩步,慢的走一步。
- 因爲我們存在環,所有兩個指針一定會相遇。
- 假設兩個相遇在某一點,則此時再讓fast從頭開始,然後slow和fast都每次走一步。
- 兩者相遇的時候就是我們的重複的數字
- why?
- 假設一開始兩者相遇的時候slow的走了N步,則fast的走了2N步
- 接下來我們分析一下這N步是由什麼構成的?
- 首先在進環前走了D步
- 從環的起點到相遇點走了K步。
- 整個環走了i圈,假設環的長度是C
- 則N = D + K + C*i
- 那麼對於2N,我們同樣有2N = D + K + C*j
- 我們消去N可以得到2D + 2K + 2Ci = D + K + Cj =>D+K = C(j-i). 注意j>i是成立的。
- 所有我們讓一個point從頭走D步。
- 那麼從相遇的點相當於走D=C(j-i)-K。
- 相當於我們差K個step可以走滿C(j-i+1)圈。
- 因爲我們相遇的點距離環的入口點是K。所有此時一定是相遇在起點的。
- 上面我們分析了爲什麼可以相遇在起點。又因爲我們兩個point在經歷起點前的一個點也一定是相同的,參照上圖。
- 所以我們可以得到當第一次兩個相同的時候,就是我們的重複數字。
- 如果說上面方法難以理解的話,我們可以在中加加上一步
- 找到相遇的點
- 計算環的長度L
- 初始化兩個point
- 第一個point先走L步
- 然後兩個point同時走,當兩個point相遇的時候就是環的入口點。
- 三種方法的代碼:
#include <vector>
#include <iostream>
using namespace std;
class Solution {
public:
int findDuplicate(vector<int>& nums) {
int size = nums.size();
for(int i=0;i<size;i++){
for(int j=i+1;j<size;j++)
if(nums[i] == nums[j])
return nums[i];
}
return -1;
}
int findDuplicateV2(vector<int>& nums){
int size = (int) nums.size();
if(size > 1){
int slow = nums[0];
int fast = nums[nums[0]];
while(slow != fast){
slow = nums[slow];
fast = nums[nums[fast]];
}
slow = 0;
while(slow != fast){
slow = nums[slow];
fast = nums[fast];
}
return slow;
}
return -1;
}
int findDuplicateV3(vector<int>& nums){
int size = (int) nums.size();
if(size > 1){
int leftCount = 0;
int rightCount = 0;
int left = 1;
int right = size - 1;
while(left < right){
int mid = (right + left) / 2;
int count = 0;
for(auto c: nums){
if(c <= mid){
count += 1;
}
}
if(count > mid){
right = mid;
}else{
left = mid + 1;
}
}
return left;
}
return -1;
}
static void solution(){
Solution solution1;
vector<int> nums = {1, 3, 4, 2, 2};
cout<<solution1.findDuplicateV3(nums)<<endl;
}
};