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.
ac版本:快慢指针,最终会进入循环。在循环中,快指针和慢指针因为速度不同,最终会相遇。
这时,令快指针从索引为0的位置再重新进入循环。
原理:设循环一遍的长度为p.第一次while循环,当快慢指针相遇的时候,可设快指针比慢指针多走了kp的长度,而快指针的速度是慢指针的2倍,所以慢指针所走的路程即为kp,设从索引为0的位置到达循环的入口位置路程为m, 慢指针在循环里走了kp,因为是一直循环,在循环里的位置就是(kp-m)%p. 当第二次while循环,快指针从索引为0的地方到达循环入口时走了m,而慢指针在循环里走了((kp-m)%p + m) = (kp-m+m)%p = 0,即两者恰好在循环入口相遇。
class Solution {
public:
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(slow != fast)
{
slow = nums[slow];
fast = nums[fast];
}
return slow;
}
return -1;
}
};
ac版本2,二分查找。原理,在无重复情况下,小于等于n的数最多只有n个。如果多余n个,说明要找的重复数字小于等于n。
class Solution {
public:
int findDuplicate(vector<int>& nums) {
int n = nums.size();
int left = 0, right = n -1;
while(left < right)
{
int count = 0;
int mid = left + (right - left)/2;
for(int i = 0; i< n; i++)
{
if(nums[i]<=mid)
count++;
}
if(count>mid) //表示数组中重复的数字是小于等于mid的,所以right = mid,而不是 mid - 1
{
right = mid;
}
else //count<=mid 表示 mid及其以下的值肯定没有重复的。所以left = mid + 1,而不是 mid
{
left = mid + 1 ;
}
}
return left;
}
};