题意
将1到n之间的一些数放到n + 1大的数组中,求出那个重复出现的元素(题目保证只有一个元素重复出现,但可能出现若干次)。
并且要求:
1. 额外空间为
O(1)
。
2. 时间复杂度小于
O(n2)
。
3. 数组是只读的,因此不能改变
思路
首先,时间复杂度小于 O(logn) ,那么我们自然能够想到是二分。可是二分什么呢?
因为所有数均在1到n之间,假设有一个数重复出现,那么x的统计次数会大于1,自然,我们可以这样考虑:假设中位数是mid,那么:
- 若 1≤x≤mid ,那么数组中对小于mid的数目进行统计应该会大于mid。
- 若 mid<x≤n ,那么对数组中小于mid的数目进行统计,应该会小于mid。
于是,我们可以得到我们的算法:对中位数mid进行二分,统计nums中小于mid的数目,若:
- cnt>mid :说明x在L到mid之间,R = mid。
- 否则:说明x在mid到R之间,L = mid。
代码
class Solution {
public:
int count(vector<int>& nums, int x) {
int cnt = 0;
for (int i = 0; i < nums.size(); i++)
if (nums[i] <= x) cnt++;
return cnt;
}
int findDuplicate(vector<int>& nums) {
int L = 1, R = nums.size(), M = (L + R) / 2;
while (L < R) {
if (R == L + 1) {
if (count(nums, L) > L) M = L;
else M = R;
break;
} else {
M = L + (R - L) / 2;
if (count(nums, M) > M) R = M;
else L = M;
}
}
return M;
}
};