题目:在一个长度为n+1的数组里所有的数字都在1~n的范围内,所以数组中至少有一个数字是重复的,请找出数组中任意一个数字,但不能修改输入的数组。
#include <stdio.h>
#include <stdlib.h>
//统计数字start 到 end 中的数字在整个数组中出现的次数
int countRange(const int* Array, int length, int start, int end)
{
int count = 0 , i = 0;
if(end < start) end = start;
if( (Array == NULL) || (length <= 0) || start > end)
{
return -1;
}
for(i = 0; i < length; i++ )
{
if( (Array[i] >= start) && (Array[i] <= end) )
{
printf("countRangecount is %d \n", count);
++count;
}
}
printf("count %d \n", count);
return count;
}
//算法思想
//长度为n+1的数组,所有数字范围都在1~n的范围内
//1.类似二分查找法,遍历整个数组,先统计1~n/2 (假设个数为 m)这些数字在数组中出现的次数
//2. 如果统计的次数大于m,可以说明重复的数字一定在1~n/2这个范围内的。反之,在后面一部分内。
//2.1 继续在1~n/2这个范围内用同样的方法寻找,直到找到最后一个范围内只有两个数字的时候,
// 再分别统计这两个数字在数组中出现的次数,出现次数为2的那个数字即是我们需要找到的哪个数。
//不修改输入的数组 所以这里用常量指针
int duplicate(const int *Array, int length)
{
int middle = 0 , i = 0;
//这里先定义两个哨兵,一个指向最小范围数字1,一个指向最大范围值n
//每一次统计,其中一个哨兵重新更新范围值(将需要查找的目标值不停的缩小范围)
int start = 1;
int end = length - 1;//数组长度为n+1, 数字范围为1 ~ n.
int count0 = 0;
if( (Array == NULL) || length <= 0)
{
return -1;
}
for(i = 0; i < length; i++)
{
if( (Array[i] <= 0) || (Array[i] > end) )
return -1;
}
while(end >= start) //跳出循环的条件
{
middle = (end - start)/2 + 1;//middle 中值不能为0
//middle = ((end - start)>>1) + start;
printf("middle is %d \n", middle);
//先把数组一分为二,先统计1到middle在整个数组中出现的次数
count0 = countRange(Array, length, start, middle);
//统计出结果,先判断是否满足函数的出口条件
if(end == start)//两个哨兵合并到一起,说明查找已经结束了
{
if(count0 > 1)
{
printf("###The duplicate number is %d \n", start);
return start;
}
else
{
break;//寻找结束,没有找到
}
}
if(count0 > (middle - start + 1))//如果统计的次数大于从start 到 middle范围的数字个数
end = middle;
else
start = middle + 1;
}
return -1;
}
int main(int argc, char *argv[]) {
int test1[] = {3,1,5,4,3,2,6,7};
duplicate(test1, sizeof(test1)/sizeof(test1[0]));
return 0;
}
运行结果:
算法时间复杂度:O(nlog(n))
算法空间复杂度:O(1)