题目一:找出数组中重复的数字
在一个长度为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;
}
运行结果为: