不修改数组找出重复的数字
题目描述:在一个长度为n+1的数组里的所有数字都在1到n的范围内,所以数组中至
少有一个数字是重复的。请找出数组中任意一个重复的数字,但不能修改输入的数组。例如,如果输入长度为8的数组{2, 3, 5, 4, 3, 2, 6, 7},那么对应的输出是重复的数字2或者3。
算法思路:
- 二分查找法: 如:{2,3,5,4,3,2,6,7} 先二分,先统计在1-4里面的数字个数,如果大于4则说明1-4里面有重复数字,否则重复数字必然在5-7里面,再对区间进行划分,直到剩一个数字且该数字出现的次数大于一,则该数字就是那个重复数字。
代码实现
#include<stdio.h>
int getDuplicate(const int* numbers, int length)
{
if (numbers == NULL || length <= 0)
{
return -1;
}
int start = 1;
int end = length - 1;
while (end >= start)
{
int middle = ((end - start) >> 1) + start;
int count = countRange(numbers,length,start,middle);
if (end == start)
{
if (count > 1)
{
return start;
}
else
{
break;
}
}
if (count > (middle - start + 1))
{
end = middle;
}
else
{
start = middle + 1;
}
}
return -1;
}
计算数组中在给定区间的元素个数
int countRange(const int* numbers, int length, int start, int end)
{
if (numbers == NULL)
{
return 0;
}
int count = 0;
for (int i = 0; i < length; i++)
{
if (numbers[i] >= start && numbers[i] <= end)
{
++count;
}
}
return count;
}
修改数组的顺序解法
思路:由于长度为n的空间中的元素都在1-n-1之间,因此一个萝卜一个坑.如果数组中没有重复的数字,则第i位置应该放置的数字为i。可以重排此数组,让对应位置上的数字对应。
- 首先判断元素是否和对应下标相等,如果相等,代表它在自己的正确位置上,不用考虑,直接判断下一个元素
- 如果不相等,说明它不在自己的位置上,此时它就有可能是哪个重复的元素,此时 分两种情况
- 情况一:如果 它和自己对应下标下的元素相等就代表它是重复元素
- 情况二:它还未回到自己的位置上去, 和它应该所在位置的元素进行交换,让它回到自己的位置上去
int duplicate(int *arr,int len)
{
if(len<0||arr==NULL)
{
return -1;
}
for(int i=0;i<len;++i)//检查输入数组的合法性
{
if(arr[i]<0||arr[i]>=len)
{
return -1;
}
}
for(int i=0;i<len;++i)
{
while(i!=arr[i])
{
if(arr[i]==arr[arr[i]])
{
return arr[i];
}
int tmp=arr[i];
arr[i]=arr[tmp];
arr[tmp]=tmp;
}
}
return -1;
}
以下几种方法空间复杂读或者时间复杂度都优点高,就不进行代码展示,读者可以通过阅读思路,简单了解一下。O(∩_∩)O
哈希表法
思路:从头到尾顺序扫描数组中的每一个数,每扫描一个数字可以用O(1)的时间在哈希表中判断是否包含此数字,如果哈希表中没有此数字就将此数字加入到哈希表中,如果哈希表中已存在此数字就找到了重复的数字。时间复杂度为O(n),但是空间复杂度也为O(n),以空间换区时间。
排序
思路:将输入的数组进行排序,遍历排序后的数组找到重复的数字(该元素和其下一个元素相等就代表该元素是重复元素)。排序一个长度为n的数字的时间复杂度为O(nlogn),所以这种方法的时间复杂度为O(nlogn)。