不修改数组查找数组中任意一个重复的数字

题目:在一个长度为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)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值