題目:
Given an array nums containing n + 1 integers where each integer is between 1 and n (inclusive), prove that at least one duplicate number must exist. Assume that there is only one duplicate number, find the duplicate one.
Note:
- You must not modify the array (assume the array is read only).
- You must use only constant, O(1) extra space.
- Your runtime complexity should be less than
O(n2)
. - There is only one duplicate number in the array, but it could be repeated more than once.
方法一:二分法
分析:
n+1個數屬於區間[1, n],必然存在重複(抽屜原則),抽象地,超過(b-a+1)個數屬於區間[a, b]必然也存在重複。比較粗魯的方式是在nums中分別搜索1~n,判斷有沒有重複,時間複雜度o(n^2)不行。一定需要遍歷數組n次麼?可不可以少一點呢?
考察n/2,遍歷nums,如果小於等於n/2的數超過n/2,那麼區間[1, n/2]中必然存在重複;如果不超過n/2,說明屬於區間[n/2+1, n]中的數(n+1-n/2)大於n/2,因此區間[n/2+1, n]中必然存在重複的數。利用這種方式可以折半查找區間,時間複雜度(nlgn)。
class Solution {
public:
int findDuplicate(vector<int>& nums) {
int first = 1, last = nums.size() - 1;//兩邊都是閉區間
while (first < last) //如果first==last,那麼first就是要找的數
{
int mid = first + (last - first) / 2;
//統計小於等於mid的數的個數
int count = 0;
for (auto p : nums)
{
if (p <= mid)
{
++count;
}
}
//分成兩個區間[first, n/2]和[n/2+1, last]
if (count > mid) //[1, 2, ..., n/2]共n/2個數,如果count > n/2,那麼必然存在重複
{
last = mid;
}
else
{
first = mid + 1;
}
}
return first;
}
};
方法二:鏈表找環 https://segmentfault.com/a/1190000003817671
假設數組中沒有重複,那我們可以做到這麼一點,就是將數組的下標和1到n每一個數一對一的映射起來。比如數組是213,則映射關係爲0->2, 1->1, 2->3。假設這個一對一映射關係是一個函數f(n),其中n是下標,f(n)是映射到的數。如果我們從下標爲0出發,根據這個函數計算出一個值,以這個值爲新的下標,再用這個函數計算,以此類推,直到下標超界。實際上可以產生一個類似鏈表一樣的序列。比如在這個例子中有兩個下標的序列,0->2->3。
但如果有重複的話,這中間就會產生多對一的映射,比如數組2131,則映射關係爲0->2, {1,3}->1, 2->3。這樣,我們推演的序列就一定會有環路了,這裏下標的序列是0->2->3->1->1->1->1->...,而環的起點就是重複的數。
所以該題實際上就是找環路起點的題,和Linked List Cycle II一樣。我們先用快慢兩個下標都從0開始,快下標每輪映射兩次,慢下標每輪映射一次,直到兩個下標再次相同。這時候保持慢下標位置不變,再用一個新的下標從0開始,這兩個下標都繼續每輪映射一次,當這兩個下標相遇時,就是環的起點,也就是重複的數。
class Solution {
public:
int findDuplicate(vector<int>& nums) {
int slow = 0, fast = 0;
do
{
slow = nums[slow];
fast = nums[nums[fast]];//每次跨過兩個結點
}
while (slow != fast);
//再定義一個慢指針從起點出發,與slow相遇在環的入口
int find = 0;
while (slow != find)
{
slow = nums[slow];
find = nums[find];
}
return find;
}
};