题目描述
在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任意一个重复的数字。 例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是第一个重复的数字2。
方法:
- 若是允许修改数组,则可以按照以下的思路:
由于数组中的数都是来自于0-n-1中的数字,数组大小为n,因此,如果我们对数组进行排序,如果没有重复的数字,则刚好是一个萝卜对一个坑,恰好第i个数字为i。否则,有的位置的数字不存在,而有的位置的数字会有好几个。
因此,我们对数组进行遍历重排,如果当前数字m不能对应自己的坐标i,则对其进行归位,用第m个数与i交换,直到此位置恢复为i。而如果第m个位置已经有m,则返回true。
直到所有数归为,则返回false。
proof: 每一次交换,都置一个数归位,如果当前i归位成功,则进行下一个数的遍历操作,如果数组中不存在重复数字,则不存在冲突直到遍历结束返回false。如果数组中存在重复数字,则在某一个数字的归位操作中,会产生冲突返回true。
class Solution {
public:
// Parameters:
// numbers: an array of integers
// length: the length of array numbers
// duplication: (Output) the duplicated number in the array number
// Return value: true if the input is valid, and there are some duplications in the array number
// otherwise false
bool duplicate(int numbers[], int length, int* duplication) {
if(numbers==nullptr || length<=0)
return false;
for(int i=0;i<length;i++)
{
if(numbers[i]<0||numbers[i]>length-1)
return false;
while (numbers[i]!=i)
{
if(numbers[numbers[i]]==numbers[i])
{
*duplication=numbers[i];
return true;
}
else
{
int temp=numbers[i];
numbers[i]=numbers[numbers[i]];
numbers[temp]=temp;
}
}
}
return false;
}
};
这道题使用了数组O(1)时间查找的性质,也使用了这道题的特殊性进行分析
时间复杂度为O(n) 因为每次都有一个归位
- 如果是不允许修改数组,则可以使用二分法进行查找,这个也是很妙,使用的是数组本身的性质。对于一个数组nums[i,j] (从数组的第i个数到数组的第j个数)以中间数m(m=(i+j+1)/2)作为标准,将数组进行计算,计算在区间[i,m]的数目。如果前一半大于(m-i+1),则在前面数组进行进一步的二分查找[i, m];若小于(m-i+1),则在后面数组[m+1, j]进行计算。
如果等于m-i+1, 则无法判断是否包含,因此我们需要对m进行细微改动(m-1)。这里要注意,如果m=i,计算[i,m]个数为1会导致countm-i+1,这说明前面部分一定不是重复数字,所以我们要判断这个时候重复数字一定在[m+1, j]中。
直到最终ij时,返回该数字。
class Solution {
public:
// Parameters:
// numbers: an array of integers
// length: the length of array numbers
// duplication: (Output) the duplicated number in the array number
// Return value: true if the input is valid, and there are some duplications in the array number
// otherwise false
bool duplicate(int numbers[], int length, int* duplication) {
int i=0,j=length-1;
if(numbers==nullptr || length<=0)
return false;
for (int i = 0; i < length; ++i){
if (numbers[i] < 0 || numbers[i] > length -1)
return false;
}
int flag=0;
int m=(j+i)/2;
while(i<=j)
{
if (flag==0)
m=(j+i)/2;
int cur=countrange(numbers, length, i, m);
if (i==j)
{
if (cur>1)
{
*duplication=i;
return true;
}
else break;
}
if(cur>m-i+1)
j=m;
else if (cur==m-i+1)
{
if(m-1<i)
{
i=m+1;
flag=0;
}
else
{
m=m-1;
flag=1;
}
}
else
i=m+1;
}
return false;
}
int countrange(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]<=end&&start<=numbers[i])
count++;
}
return count;
}
};