题目:在一个长度为n的数组里的所有数字都在0~n-1的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。
这里的方式其实不局限于一两种,可以先排序然后再依次的进行比较。书中提供了一种比较简单的方式。
注意这里不是要找出所有重复的数字,只要找到一个就行了。
由于数组中的数字都是在0~n-1范围内的,如果数组中没有重复的数字,那么数组排序以后的数字 i 将出现在data[i]的位置。
一边在重排数组,一边也在查找重复的数字,这样的效率会比较高。
直接上代码:
#include "../common.h"
bool findDuplicateNumbers(int *dataNumbers, int length, int *duplicate)
{
if(dataNumbers == nullptr)
{
return false;
}
for(int i = 0; i < length; ++i)
{
if(dataNumbers[i] < 0 || dataNumbers[i] > length - 1)
{
return false;
}
}
for(int i = 0; i < length; ++i)
{
while(dataNumbers[i] != i)
{
if(dataNumbers[i] == dataNumbers[i])
{
*duplicate = dataNumbers[i];
return true;
}
int temp = dataNumbers[i];
dataNumbers[i] = dataNumbers[temp];
dataNumbers[temp] = temp;
}
}
return false;
}
int main()
{
int data[7] = {2,3,1,0,2,5,3};
int duplicate = -1;
if(findDuplicateNumbers(data,7,&duplicate) != false)
{
printf("duplicate:%d\n",duplicate);
}
else
{
printf("No duplicate data !!\n");
}
}
结果:
duplicate:2
如果要找出所有的重复数字,也比较简单,只需要继续匹配,并把已经找出的重复数字记录到另外的数组中就可以了。
现在要求,不能改变原数组!!且数组的每个值在0~n-2中,也就意味着数组中必然有至少一个重复数字。
第一种做法:把原数组拷贝到新的数组之中,用副本的数组进行如上的操作。
第二种做法:用二分法进行查找。把0~n-2分为两部分:0~m和m~n-2,先匹配0~m在data数组中存在的次数,如果次数>m,则说明数组中在0~m之间有重复的数字,接下来再分为0~m/2和m/2到m。依次这样下去,直到找到最后的重复数字为止。
代码如下:
#include "../common.h"
/*计算在start至end的数字总共出现了多少次*/
int countRange(const int *dataNumbers,int length,int start,int end)
{
if(dataNumbers == nullptr)
{
return 0;
}
int count = 0;
for(int i = 0; i < length; ++i)
{
if(dataNumbers[i] <= end && dataNumbers[i] >= start)
{
++count;
}
}
return count;
}
bool findDuplicateNumbers(const int *dataNumbers, int length, int *duplicate)
{
if(dataNumbers == nullptr)
{
return false;
}
for(int i = 0; i < length; ++i)
{
if(dataNumbers[i] < 0 || dataNumbers[i] > length - 2)
{
return false;
}
}
int start = 0;
int end = length - 2;
while(end >= start)
{
int middle = ((end - start) >> 1) + start;
int count = countRange(dataNumbers,length,start,middle);
if(end == start) //最后一组
{
if(count > 1)
{
*duplicate = end;
return true;
}
else
{
return false;
}
}
if(count > (middle - start + 1)) //重复的数字,就在这半段继续二分法找
{
end = middle;
}
else //上半段没有重复的,匹配另外半段
{
start = middle + 1;
}
}
return false;
}
int main()
{
int data[8] = {0,4,1,2,4,5,6,3};
int duplicate = -1;
if(findDuplicateNumbers(data,8,&duplicate) != false)
{
printf("duplicate:%d\n",duplicate);
}
else
{
printf("No duplicate data !!\n");
}
}
结果:
duplicate:4
这种二分法没有办法找出所有的重复数字!!!
如果不确定数组中是否有重复的数字,也就是把值限定在0~n-1中时,二分法查找重复数字是无效的。如当n为8时,数组为[0,1,3,3,4,5,6,7],数组中少了2,重复了3,但是无法用二分法查找出来。因为在这个数组中,本身就可以没有重复的数字。二分法只能用于确定\查找,无法用于判断。