题目
在一个长度为n+1的数组里的所有数字都在1~n的范围内,所以数组中至少有一个数字是重复的。请找出数组中任意一个重复的数字,但不能修改输入的数组。例如,如果输入长度为8的数组{2,3,5,4,3,2,6,7},那么对应的输出是重复的数字2或者3。
思路
借鉴二分查找的思想,将数字1~n拆分成1~m和m+1~n两部分,如果数字范围1~m中数字个数大于m,则重复数字在1~m中间,否则重复数字一定在数字范围m+1~n中。基于二分查找法不能找到全部的重复数字,例如{2,2,3,3,4,5,6,7}中数字区间为1~2的范围内2出现两次,但1没有出现,不能确定是每个数字出现一个还是某个数字出现两次。
高明解法:
首先将数组一分为二,假设数组有8个元素。那么元素大小都在 0 ~ 7 之间。一分为二,如果没有重复,四个元素在 0 ~ 3 之间,四个在 4 ~ 7 之间。假设数组为arr[8] = {1, 0, 2, 3, 3, 4, 5, 6},统计0 ~ 3 之间的元素有5个, 统计 4 ~ 7 之间的元素有3个。说明,在 0 ~ 3 之间肯定有重复元素。接下来划分 0 ~ 3的5个元素{1, 0, 2, 3, 3}。 0 ~ 1 之间有两个元素, 2~3之间有三个元素。再划分2 ~ 3之间的元素,2有一个,3有两个,所以重复的是元素3。
#include <iostream>
#include <vector>
using namespace std;
class Solution{
public:
int duplication(vector<int> vec)
{
// 空数组
int length = vec.size();
if(vec.size() == 0)
return -1;
// 数字超界
for(int i =0;i<length;++i)
{
if(vec[i]<1 || vec[i]>length-1)
return -1;
}
// 定义数字范围
int begin = 1;
int end = length-1;
// 指定数字范围内的数字个数
while(begin<=end)
{
// 计算数字范围的中点
int mid = (begin + end)>>1;
// 统计指定数字范围内的数字个数
int count = countrange(vec,begin,mid,length);
if(end > begin)
{
// 更新数字范围
if(count>(mid - begin + 1))
end = mid;
else
begin = mid + 1;
}
else
{
if(count > 1)
return begin;
else
break;
}
}
return -1;
}
int countrange(vector<int> vec,int begin,int end,int length)
{
int count=0;
for(int i=0;i<length;++i)
{
if(vec[i]>=begin && vec[i]<=end)
++count;
}
return count;
}
};