Description:
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],证明这个数组里面至少有一个整数是重复出现过的。假设这个重复出现的整数只有一个,并且需要把这个整数找出来。
注意:
1.不能改变数组,数组是只读的。
2.不能使用额外的空间,即空间复杂度为O(1)
3.时间复杂度需要在O(n^2)以下
4.只有一个重复的整数,但是这个整数会出现2次或者2次以上。
得到这个题目之后,很容易就知道这个是鸽巢原理,但是我们不能使用额外的空间来解决这个问题,这就有些棘手。
同时我们也很容易想到暴力求解:两个循环,遍历数组,但是时间复杂度为O(n^2),也不满足题目要求。
我们可以观察到,数组里面的元素都是正整数,并且正整数的最大值不会超过数组的长度。
并且本题由于是查找类型,所以我们就想到有哪些查找算法:二分,查找树,哈希......符合题意的只有二分,其他的需要额外空间。
所以我们就从二分+正整数最大值这两个条件来着手解决这个问题:
我们把正整数取值区间的中间值找出来,并且遍历数组一次,把小于等于这个中值的元素个数记录下来(count)。
如果这个count大于等于中值,那么就意味着重复元素出现在区间[1,中间值]之间,如果小于这个中值,那么就意味着重复元素出现在(中间值, 最大值]之间。
这样我们就利用了二分来减少时间复杂度O(NlogN),我们需要一直进行这种比较和计数,直到子数组的元素个数为1,并且那个数就是我们需要寻找的元素。
int findDuplicate(vector<int>& nums) {
int begin = 1;
int end = nums.size() - 1;
while (begin < end) {
int count = 0;
int mid = (begin + end) / 2;
for (int num : nums) {
if (num <= mid) {
count++;
}
}
if (count <= mid) {
begin = mid + 1;
} else {
end = mid;
}
}
return begin;
}
按照题目的要求,就可以得出上面代码。
有过的思路:元素索引,位图。