题目
面试题3(一):找出数组中重复的数字
在一个长度为n的数组里的所有数字都在0到n-1的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。例如,如果输入长度为7的数组{2, 3, 1, 0, 2, 5, 3},那么对应的输出是重复的数字2或者3。
面试题3(二):不修改数组找出重复的数字
在一个长度为n+1的数组里的所有数字都在1到n的范围内,所以数组中至少有一个数字是重复的。请找出数组中任意一个重复的数字,但不能修改输入的数组。例如,如果输入长度为8的数组{2, 3, 5, 4, 3, 2, 6, 7},那么对应的输出是重复的数字2或者3。
代码
#pragma once
#include <iostream>
using namespace std;
// 题目一:找出数组中重复的数字 0~n-1 长度n
/**
* 一维数组在内存中连续存储,可以根据下标定位对应的元素
*/
bool duplicate(int* number, int length, int* duplication) // duplication为重复值
{
// 边界条件 无效输入检查
if (number == nullptr || length <= 0)
return false;
for (int i = 0; i < length; ++i)
{
if (number[i]<0 || number[i]>length - 1)
return false;
}
for (int i = 0; i < length; ++i)
{
while (number[i] != i)
{
if (number[i] == number[number[i]])
{
*duplication = number[i];
return true;
}
// 不相等则交换
int tmp = number[i];
number[i] = number[tmp];
number[tmp] = tmp;
}
}
return false;
}
// 题目二:不修改数组找出重复的数字 1~n 长度n+1(要多1)
/**
* 一维数组在内存中连续存储,可以根据下标定位对应的元素
* 查找任意一个重复数字,可利用二分查找算法
* 若是全部重复数字,可利用辅助数组
*/
int getDuplication(int* number, int length)
{
if (number == nullptr || length <= 0)
return -1;
for (int i = 0; i < length; ++i)
{
if (number[i]<1 || number[i]>length - 1) // 1~length-1
return -1;
}
// 二分查找
int start = 1;
int end = length - 1;
while (end > start)
{
int mid = (start + end) / 2;
// 统计在整个数组里该区间的数字的数目
int count = countRange(number, length, start, mid);
if (end == start)
{
if (count > 1)
return start;
else
break;
}
if (count > mid - start + 1)
end = mid;
else
start = mid + 1;
}
return -1;
}
// 统计在整个数组里该区间的数字的数目
int countRange(int* number, int length, int start, int end)
{
int count = 0;
for (int i = 0; i < length; ++i)
if (number[i] >= start && number[i] <= end)
++count;
return count;
}
总结
(题目一)将下标为 i 的数字 m 和下标为 m 的数字相比较,不相等则交换,直到相等为止。时间复杂度 O(n)。
(题目二)二分法划分数字区间 1~n 为 1~m(中间值)和 m+1~n,计算区间内数字在数组中出现次数,若大于该区间差值则应在该区间继续二分查找。时间复杂度 O(nlogn)。