问题:在不编辑给定数组的情况下,在长度为n+1的数组中找到任意一个重复的数字,所有数字在1~n范围内。
输入:数组,数组的长度
输出:重复的数字
思路:由于不能修改原有数组,面试题3_1的算法思路不能使用。根据本题的条件,必定会存在一个重复的数字,如果将开辟一个大小为n的数组,将原数组的数字放到新数组下表为其值的位置,花O(n)时间复杂度就能找到重复的数字。但是,其空间复杂度也为O(n)。
本文牺牲时间复杂度换取空间复杂度,类似二分查找的算法思路如下:
利用数组下标的性质(根据下标定位对应的元素)。
把1~n的数字(长度为n+1)从中间的数字m分为两部分,前面一半为1~m,后面一半为m+1~n。如果1~m的数字的数目超过m,那么这一半的区间里一定包含重复的数字。此时,继续把包含重复数字的区间一分为二,直到找到一个重复的数字。
代码:
#include <iostream>
using namespace std;
int countRange(const int* numbers, int length, int start, int end);
int getDuplication(const int* numbers, int length)
{
if(numbers==nullptr || length<=0)
return -1;
int start = 1;
int end = length-1;
while(end >= start)
{
int middle = ((end - start)>>1)+start;
int count = countRange(numbers, length, start, middle);
if(end==start)
{
if(count> 1)
return start;
else
break;
}
if(count>(middle-start+1))
end =middle;
else
start=middle+1;
}
return -1;
}
int countRange(const int* numbers, int length, int start, int end)
{
if(numbers==nullptr)
return 0;
int count = 0;
for(int i=0; i<length; i++)
{
if(numbers[i]>=start&&numbers[i]<=end)
count+=1;
}
return count;
}
int main(int argc, char const *argv[])
{
int numbers[] = {0,3,2,1,3};
int result;
result = getDuplication(numbers,5);
if(result!=-1)
cout<<result<<endl;
else
cout<<"not found"<<endl;
return 0;
}
复杂度分析:时间复杂度为O(nlogn),空间复杂度为O(1).
扩展:本文算法不能找到所有重复的数字。
例如,数组{2,3,5,7,4,3,2,6},1~2的范围的数字出现2次,本文算法不能确定1和2各出现一次,还是某个数字出现了2次。