1 - n 里面有n + 1个数,由抽屉原理可知,最少有两个数相同,我们用递归的思想来做,把整个区间一分为二,以数值来划分(而不是以数组下标来分),即左半边是所有数值在1 - n / 2之间的数,右半边是数值在n / 2 + 1到n之间的数,因为整个区间中至少存在两个数是相同的,所以,左边或右边至少有一边,数的个数大于坑的个数,即有两个数在一个坑里。(简单说就是,限定死了1- n 这n个位置,把题目给的n+1个数往里面放,必然至少有一个位置放了两个)
每次我们可以把整个区间的规模缩小一半,保证我们这一半的区间里面是有答案的。
每次把区间划分一半,然后统计左右两半边数的个数,找一边(左右均可)它数的个数大于坑的个数,然后选择这一边,一共长度为n的区间,总共划分logn次,每次求左右两半边数的个数需要O(n)扫一遍,所以整个的时间复杂度是O(n)。
class Solution {
public:
int duplicateInArray(vector<int>& nums) {
int l = 1, r = nums.size() - 1;
while (l < r)
{
int mid = l + r >> 1;
// 统计nums数组中,数值在l-mid之间的数的个数
int s = 0;
for (int i = 0; i < nums.size(); ++ i)
if (nums[i] >= l && nums[i] <= mid) s ++;
// 若s > mid - l + 1,由抽屉原理知,重复的元素在左半部分
if (s > mid - l + 1) r = mid;
else l = mid + 1;
}
return r;
}
};