题目要求
在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任意一个重复的数字。 例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是第一个重复的数字2。
解题分析
使用蛮力法,高额的时间代价是我们得不偿失的,还可以想到使用哈希表,通过构建辅助数组,然后遍历一遍数组的元素即可,时间复杂度O(n),但是需要额外的O(n)辅助空间。
下面提出一种方法,在保证时间O(n)的基础上,空间复杂度为O(1),我们叫交换元素法。因为长度为n的数组里的所有数字都在0到n-1的范围内,正常没有重复的情况下,我们可以把他们按照下标顺序排成0,1,2…n-1的样子。
算法的主要思想:通过遍历数组元素,判断当前数组中的元素和数组下标是不是一致的,如果不一致那么就把当前元素放到下标属于他的位置,在放置之前,我们要先判断一下这两个数是不是相等,如果相等的话,我们就找到了一个重复元素,不相等的话直接交换即可。
主要代码c++
// 采用交换法,time O(n), space O(1) 但是改变了数组,
class Solution {
public:
// Parameters:
// numbers: an array of integers
// length: the length of array numbers
// duplication: (Output) the duplicated number in the array number
// Return value: true if the input is valid, and there are some duplications in the array number
// otherwise false
bool duplicate(int numbers[], int length, int* duplication) {
// 输入数组大小不合法
if(length<=0 || numbers == nullptr)
return false;
// 输入数组元素不合法 保证数组元素在(0,n-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) // 因为一次交换可能不能保证所有的numbers[i]都在自己的位置上
{
if(numbers[i] == numbers[numbers[i]]) // 需要交换的元素,关键步骤
{
*duplication = numbers[i];
return true;
}
else
swap(numbers[i],numbers[numbers[i]]);
}
}
return false;
}
};
总结
交换元素法很显然相比蛮力法和哈希表法要高效,简洁。但是需要注意的是,我们改变了数组的结构,如果遇到题目不允许我们修改数组结构的时候,我们可以借用二分查找的方法来解答,具体的解法下个例子讲~