目录
题目描述
在一个长度为n的数组里的所有数字都在0~n-1的范围内。数组中某些数字重复的,但不知道有几个重复的数字,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。例如:如果输入长度为7的数组{2,3,1,0,,2,5,3},那么对应的输出是重复的数字2或者3.
方法一
- 思路
由于数组中的元素在数组下标范围内,因此如果没有重复的话,对其排序时,每个元素所在的位置是提取知道的,例如3就是数组A[3]的位置上。因此可以通过交换在O(n)时间内对其进行排序,如果在交换过程中遇到重复的数字,那么就找到了我们要的答案,否则没有。
- C++实现
/*******************************************************************
Function:duplicate_1
Input:
numbers:数组
length;数组numbers的长度
duplication:用于保存重复的数字
********************************************************************/
bool duplicate_1(int numbers[],int length,int* duplication){
if(NULL==numbers||length<=1)
return false;
for(int i=0;i<length;i++){
if(numbers[i]<0||numbers[i]>length-1)
return false;
}
for(int i=0;i<length;i++){
while(numbers[i]!=i){
//检查将要执行交换的两个元素,若相同则直接返回
if(numbers[i]==numbers[numbers[i]]){
*duplication = numbers[i];
return true;
}
swap(numbers[i],numbers[numbers[i]]);
}
}
return false;
}
方法二
- 思路
在原始的数组不可以被修改的条件下,可以使用类似于方法一的方法,但是新申请一个数组,然后将数组重排中新数组中。此时的时间复杂度为O(n),空间复杂度也为O(n).
- C++实现
bool duplicate_2(const int numbers[],int length,int* duplication){
if(NULL==numbers||length<=1)
return false;
for(int i=0;i<length;i++){
if(numbers[i]<0||numbers[i]>length-1)
return false;
}
int* a = new int[length];
for(int i=0;i<length;i++)
a[i] = -1;
for(int i=0;i<length;i++){
if(a[numbers[i]]==numbers[i]){
*duplication = numbers[i];
return true;
}
a[numbers[i]] = numbers[i];
}
delete[] a;
return false;
}
方法三
- 思路
在原始数组不可以被修改的情况下,类似于折半查找,例如数组长度为n,即元素介于0到n-1,先统计数组中的元素介于0到n/2的元素的个数,如果个数大于n/2+1,说明存在重复的元素。然后再考察0到n/4,依次类推。当区间收缩到单个元素,而且该元素的个数大于1,就得到了重复的数字。折半查找的时间复杂度为O(logn),统计区间内元素的个数的时间复杂度O(n),故总的时间复杂度为O(nlogn).
- C++实现
/*******************************************************************
Function:countRange
Description:统计数组numbers中在区间[l,h]中元素的个数
********************************************************************/
int countRange(const int numbers[],int length,int l,int h){
if(NULL==numbers)
return 0;
int count = 0;
for(int i=0;i<length;i++){
if(numbers[i]>=l&&numbers[i]<=h)
count++;
}
return count;
}
bool duplicate_3(const int numbers[],int length,int* duplication){
if(NULL==numbers||length<=1)
return false;
for(int i=0;i<length;i++){
if(numbers[i]<0||numbers[i]>length-1)
return false;
}
int l = 0,h=length;
while(l<=h){
int mid = (l+h)/2;
int count = countRange(numbers,length,l,mid);
//收缩到单个数字且这个数组在numbers中不止一个
if(l==h&&count>1){
*duplication = numbers[l];
return true;
}
if(count>(mid-l+1))
h = mid;
else
l = mid+1;
}
return false;
}
测试代码
int main(){
int A[] = {3,1,2,0,2,5,3};//测试用例1
//int* A = NULL;//测试用例2:空指针
//int A[] = {6,3,4,5,2,1,0};//测试用例3:无重复元素
int* duplication = new int;
if(duplicate_3(A,sizeof(A)/sizeof(int),duplication)){
cout<<*duplication;
}else{
cout<<"no duplication";
}
delete duplication;
}