[LeetCode] 287. Find the Duplicate Number(Hard)
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:
1.You must not modify the array (assume the array is read only).
2.You must use only constant, O(1) extra space.
3.Your runtime complexity should be less than O(n2).
4.There is only one duplicate number in the array, but it could be repeated more than once.
不考虑约束条件,常规做法有以下三种:
1.暴力 time-
O(n2)
space-
O(1)
遍历数组,用一个变量存储当前数字,再来一层遍历,找到相同数字即可。
2.哈希表 time-
O(n)
space-
O(n)
遍历数组时,用当前数字作为哈希表数组的下标,该值改为真,若已存在于表中,即找到。
3.排序法 time-
O(nlogn)
space-
O(1)
先对数组排序,接着遍历,遇到前后数字相同即找到。时间空间都满足,但是要修改数组不符合要求。
可行算法有以下两种:
1.二分查找 time-
O(nlogn)
space-
O(1)
对于这n+1个数,如果重复数字在1~
1+n2
范围内,即整个数组内大于搜索值域均值
1+n2
的数的个数大于
n2
,接下来缩短搜值域范围可缩小至1~
1+n2
,迭代下去,即可得到所求数字。
class Solution {
public:
int findDuplicate(vector<int>& nums) {
int s,e,count,mid,i;
for(s=0,e=nums.size()-1;s<=e;count>mid?e=mid-1:s=mid+1){
count=0;
mid=s+(e-s)/2;
for(i=0;i<nums.size();i++)
if(nums[i]<=mid)
count++;
}
return s;
}
};
2.环路起点检测 time-
O(n)
space-
O(1)
从数组首位开始,将值作为数组下标映射到数组下一值,这样,由于数组值中有一重复数字,所以在重复值出现时会陷入循环。根据Floyd’s cycle-finding algorithm,使用快慢两个指针,在第一次搜索时快指针每轮映射两次,而慢指针一次,这样当快慢指针相等时,即发现环路,但此时指针数值不一定是所求重复数字。这时将快指针置零,从数组首位开始遍历,且变为每轮映射一次,直到与原来的慢指针相遇,此时就是所求重复数字。关于时间复杂度的推算,是第一次发现环路的n加上第二次找到重复数字的n,可得。
P.S.这种方法使用按理说应该有一个前提条件的,那就是原始数组里不能有nums[n]=n的情况,否则会陷入自身死循环,可能得到错误答案。题干里并没有给出这个说明,但是从测试全部通过来看这个条件是隐含的。
class Solution {
public:
int findDuplicate(vector<int>& nums) {
int slow=0,fast=0;
do{
slow=nums[slow];
fast=nums[nums[fast]];
} while(slow!=fast);
fast = 0;
while(fast!=slow){
slow=nums[slow];
fast=nums[fast];
}
return fast;
}
};