数组中的重复数

题目一:找出数组中重复的数字

    在一个长度为n的数组里的所有数字都在0~n-1的范围内。数组中某些数字是重复的,但是不知道有几个数字重复,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是重复的数字2或者3。

 

分析:

方案1:先排序,再查找。先将数组排序,然后比较相邻元素,如果相等就找到重复数,时间复杂度为O(nlogn)。

方案2:借用一个哈希表来查找。首先遍历整个数组,每遍历一个元素都与哈希表中的值比较,如果没有就把该元素加入到哈希表中,如果有就找到重复数了。时间复杂度是O(n),但是消耗了一个大小为O(n)的哈希表,是典型的以空间换取时间的做法。

方案3:利用数组元素与数组元素下标的关系来查找。遍历整个数组,当扫描到下标为i的数字(m)时,先判断这个数字是否为i,如果是则继续遍历;如果不是则再判断下标为m的数字是否为m,如果是则找到重复数了,否则就把第i个数字与第m个数字交换位置,直到找到重复数。

【例1】在数组{2,3,1,0,2,5,3}中查找。

第一次:第一个数字2与它下标0不相等,也与下标为2的数字不相等,则下标0与下标2的数字交换,交换后的数组为{1,3,2,0,2,5,3}。

第二次:下标为0的数字是1,与下标0还是不相等,继续把它和它(1)下标的数字3比较,不相等,则下标0和下标1的数字交换,交换后的数组为{3,1,2,0,2,5,3}。

第三次:下标为0的数字是3,与下标0还是不相等,继续把它和它(3)下标的数字0比较,不相等,则下标0与下标3的数字交换,交换后的数组为{0,1,2,3,2,5,3}。

第四次:下标为0的数字也是0,相等,则继续往后遍历。

第五步:下标为1的数字也是1,相等,则继续往后遍历。

第六步:下标为2的数字也是2,相等,则继续往后遍历。

第七步:下标为3的数字也是3,相等,则继续往后遍历。

第八步:下标为4的数字2,与下标4不相等,继续把它和它(2)下标的数字2比较,相等,则找到重复数了。

 

程序:

#include<stdio.h>
#include<stdlib.h>


int GetDuplication(int arr[], int len)
{
	//处理异常情况
	if (arr == NULL || len <= 0)
	{
		return -1;
	}
	//限定范围
	for (int i = 0; i < len; i++)
	{
		if (arr[i] < 0 || arr[i] >= len)
		{
			return -2;
		}
	}
	//查找
	for (int i = 0; i < len; i++)//遍历整个数组
	{
		while (arr[i] != i)//当数组元素不等于下标时才执行循环
		{
			if (arr[i] == arr[arr[i]])//找到重复数
			{
				return arr[i];
			}
			else
			{
				//交换
				int tmp = arr[i];
				arr[i] = arr[tmp];
				arr[tmp] = tmp;
			}
		}
	}
	return -3;
}

int main()
{
	int arr[] = { 1, 3, 2, 0, 2, 5, 3 };
	int len = sizeof(arr) / sizeof(arr[0]);
	for (int i = 0; i < len; i++)
	{
		printf("% d", arr[i]);
	}
	printf("\n");
	int ret = GetDuplication(arr, len);
	printf("% d\n", ret);
	system("pause");
	return 0;
}

运行结果为:

 

题目二:不修改数组找出重复的数

在一个长度为n+1的数组里的所有数字都在1~n的范围里,所以数组中至少有一个数字是重复的。请找出数组中任意一个重复的数字,但是不能修改输入的数组。例如,如果输入长度为8的数组{2,3,5,4,3,2,6,1,7},那么对应的输出是重复的数字2或者3。

 

分析:假如没有重复数字,那么在1~n的范围里只有n个数字。由于数组里包含超过n个数字,所以数组里一定包含重复的数字。

【例2】在数组{2,3,5,4,2,6,1,7}中找出重复数。

在长度为8,数字范围都在1~7的数组中必有重复数。

第一步:以中间数字4将数组分成1~4和5~7两部分。

第二步:统计数字1~4在数组中出现的次数,是5次,可知重复数必然在这部分。

第三步:再将数字1~4分成两部分,一部分是1和2,另一部分是3和4,统计这两部分的数字在数组中出现的次数,1和2在数组中出现了3次,可知这部分含有重复数。

第四步:再将数字1~2分成1和2两部分,其中2出现了两次,所以重复数是2。

程序为:

#include<stdio.h>
#include<stdlib.h>

//统计一定范围内数字的个数
int CountNum(const int arr[], int start, int end, int len)
{
	int count = 0;
	for (int i = 0; i < len; i++)
	{
		if (arr[i] >= start && arr[i] <= end)
		{
			count++;
		}
	}
	return count;
}

//获取数组中的重复数
int GetDuplication(const int arr[], int len)
{
	if (arr == NULL || len <= 0)
	{
		return -1;
	}
	int start = 1;
	int end = len - 1;
	while (start <= end)
	{
		int mid = start + ((end - start) >> 1);
		int count = CountNum(arr, start, mid, len);
		//当只剩下一个数时
		if (start == end)
		{
			if (count > 1)
			{
				return start;
			}
			else
			{
				return -2;
			}
		}
		//当有多个数时
		if (count > (mid - start + 1))
		{//重复数在前半部分
			end = mid;
		}
		else
		{//重复数在后半部分
			start = mid + 1;
		}
	}
	return -3;
}

int main()
{
	int arr[] = { 2, 3, 5, 4, 2, 6, 1, 7};
	int len = sizeof(arr) / sizeof(arr[0]);
	for (int i = 0; i < len; i++)
	{
		printf("% d", arr[i]);
	}
	printf("\n");
	int ret = GetDuplication(arr, len);
	printf("% d\n", ret);
	system("pause");
	return 0;
}

运行结果为:

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值