题目:
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.
翻译:
一个数组的取值是1-n,数组中会有一个重复出现的数字,但是这个数字可以重复出现多次。找出这个数字,并且尽量不要改变原数组,时间复杂度O(N^2),空间复杂度O(1)。
思路:
一开始我没看到重复数字能够出现多次,还想着用位运算来解这道题,囧。。。
然后考虑用折半查找,代码也很容易写,但是折半查找需要对原数组进行排序,这样的话就修改了原数组。但是这个思路也是可以通过的。代码:
class Solution {
public:
int findDuplicate(vector<int>& nums) {
sort(nums.begin(),nums.end());
return binarysearch(nums,0,nums.size()-1);
}
int binarysearch(vector<int>& nums,int start,int end)
{
if(start==end)
return -1;
if(nums[start]==nums[end])
return nums[start];
if(end-start==1)
return -1;
int mid=(start+end)/2;
int k=-1;
k=binarysearch(nums,start,mid);
if(k!=-1)return k;
k=binarysearch(nums,mid,end);
if(k!=-1)return k;
return -1;
}
};
后来看到一个大神的代码,我的理解是利用两个标识指向数组的两个位置,slow和fast,第一次循环,slow每次移动一步,fast每次移动两步,直到两者相遇,相遇之后,再将fast设为0,重新进行一组循环,此循环中每次fast和slow每次都只移动一步。直到两个标识相遇,返回相应的值。这个做法有一种追击问题的意思,代码贴出来跟大家分享,其实我也理解的不是很深刻。
int findDuplicate(vector<int>& nums)
{
if (nums.size() > 1)
{
int slow = nums[0];
int fast = nums[nums[0]];
while (slow != fast)
{
slow = nums[slow];
fast = nums[nums[fast]];
}
fast = 0;
while (fast != slow)
{
fast = nums[fast];
slow = nums[slow];
}
return slow;
}
return -1;
}
结果:
后一种做法的结果很高效,见下: