概述
题目一:找出数组中重复的数字
在一个长度为n的数组里的所有数字都在0~n-1的范围内。数组中某些数字是重复的,但不知道有哪几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。例如,输入长度为7的数组{2,3,1,0,2,5,3},那么对应输出是重复的数字2或者3。
思路:
常规的办法可以将数组进行排序,复杂度为O(nlogn),然后遍历排序之后的数组,输出重复的数字。还有一种思路就是利用hash表,用空间换时间,遍历数组中的元素,在hash表对应的地方进行标记,最后遍历hash表就可以找到重复的元素。这里提供一种方法,可以注意到n个元素如果互不相同,那么排好序的结果就是元素的值等于元素下标的值。比如长度为7的数组不含有相同的元素,排序结果为{0,1,2,3,4,5,6}。就是从第一个元素开始遍历数组,当数组元素的值与元素的下标值不同时,比如a[2]=3,那么将a[2]和a[3]进行比较,如果a[3]也等于3,那么就找出了一个重复的数字。如果a[3]=4,那么就将a[2]和a[3]的值交换一下,a[3]=3,a[2]=4,再将a[2]和a[4]进行比较,如此循环往复,依次遍历数组中的元素就可找出其中重复的元素。
代码:
bool duplicate(int numbers[], int length, int* duplication)
{
//考虑数组为空数组的特殊情况
if (numbers == nullptr || length <= 0)//这里要十分注意,nullptr代表空指针,而null在编译的时候会被当做数组0,nullptr则不会
{
return false;
}
//考虑输入的数组元素不符合规定的范围的情况
for (int i = 0; i < length; i++)
{
if (numbers[i]<0 || numbers[i]>length - 1)
return false;
}
for (int i = 0; i < length - 1; i++)//和++i没区别
{
while (numbers[i]!=i)
{
if (numbers[i] == numbers[numbers[i]])
{
*duplication = numbers[i];
return true;
}
//交换numbers[i]和numbers[numbers[i]]
int temp = numbers[i];
numbers[i] = numbers[temp];
numbers[temp] = temp;
}
}
return false;
}
题目二:不修改数组找出重复的数字
在一个长度为n+1的数组里的所有数字都在1~n的范围内。所以数组中至少有一个数字是重复的。请找出数组中任意一个重复的数字,但是不能修改输入的数组。例如,如果输入长度为8的数组{2,3,5,4,3,2,6,7},那么对应的输出是重复的数字2或者3。
思路:
这道题常规思路依旧可以采用以空间换时间的hash方法。这里还有另外一种思路,就是把1~n的数字分为两部分,1~m和m+1~n。如果1~m的数字的数目超过m,那么这一半的区间里一定包括重复的数字:否则另一半m+1~n的区间里一定包含重复的数字,我们可以继续把重复的区间一分为二,直到找到一个重复的数字。但是这个代码有一个缺点,就是不能找全重复的数字,比如1~2的区间内,数字总数为2,但是可能是0个1和2个1这种情况。如果输入长度为n的数组,那么函数countRange将被调用O(logn)次,每次需要O(n)的时间,总的复杂度为O(nlogn),空间复杂度为O(1),相当于用时间换取了空间。所以做题的时候要向面试官问清楚,能不能改动原始数据,以及在时间复杂度和空间复杂度上更侧重于哪一个。
代码:
int getDuplication(const int*numbers, int length)
{
if (numbers == nullptr)
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 == nullptr)
return 0;
int count = 0;
for (int i = 0; i < length; i++)
{
if (numbers[i] >= start && numbers[i] <= end)
count++;
return count;
}
}
复习:
第一题的思路还是比较好想,第二题的思路有点巧妙,分组考虑的思想要好好领悟一下。还有函数的参数需要什么,不需要什么要考虑清楚。
二刷代码:
//第一题
bool duplicate(int numbers[], int length, int *duplication)
{
//考虑输入的数组为空,数组长度小于1
if (numbers == nullptr || length <= 0)
return false;
//考虑数组元素的值要在0到length-1的范围内
for (int i = 0; i < length; i++)
{
if (numbers[i]<0 || numbers[i]>length - 1)
return false;
}
for (int i = 0; i < length; i++)
{
while (numbers[i]!=i)
{
if (numbers[i] = numbers[numbers[i]])
{
*duplication = numbers[i];
return true;
}
//交换numbers[i]和numbers[numbers[i]]
int temp = numbers[i];
numbers[i] = numbers[numbers[i]];
numbers[numbers[i]] = temp;
}
}
return false;
}
//第二题
int getDuplication(const int*numbers, int length)
{
if (numbers == nullptr || length <= 0)
return -1;
int start = 1, end = length - 1;
while (end>=start)
{
int middle = start + (end + start) >> 1;
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 == nullptr || length <= 0)
return -1;
int count = 0;
for (int i = 0; i < length ; i++)
{
if (numbers[i] >= start && numbers[i] <= end)
++count;
}
return count;
}
最后
以上就是瘦瘦小松鼠为你收集整理的剑指offer面试题3——数组中的重复数字的全部内容,希望文章能够帮你解决剑指offer面试题3——数组中的重复数字所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复