题目一 找出数组中重复的数字
在一个长度为n的数组里的所有数字都在0~n-1的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任意一个重复的数字。例如,如果输入长度为7的数组{2,3,1,4,2,5,3} 那么对应的输出是重复的数字2或3。
方法1 哈希表
时间复杂度O(n),空间复杂度O(n), 额外空间用于构建哈希表
思想:建立一个哈希表,然后对当前数组中元素遍历,若当前所遍历的元素不在哈希表中,则放入,若存在即为重复数字。
对应关系【当前元素值 —>哈希表的索引】。 例: 当前遍历位数字2, 如果hash[2]=true。表示已经2存在哈希表中。
代码如下:
# include <iostream>
# include <vector>
# include <memory>
using namespace std;
bool duplicate(int numbers[], int length, int* duplication){
if(numbers == nullptr || length <= 1 ) return false; // 等于1就是没重复
int *hashTable = new int[length];
for(int i=0;i<length;++i)
{
if(hashTable[numbers[i]]== true) // 说明哈希表中已经存在此数
{
*duplication = numbers[i];
return true;
}
else
{
hashTable[numbers[i]] = true;
}
}
return false;
}
int main(){
int numbers[] = {2,3,1,0,2,5,3};
int *duplication = new int(0);
int length = sizeof(numbers)/sizeof(numbers[0]);
duplicate(numbers,length, duplication);
cout<<*duplication<<endl;
return 0;
}
方法2 交换位置法
时间复杂度O(n),空间复杂度O(1) 在输入上进行操作,不额外分配空间
数组中的数字都在0~ n-1的范围内,如果没有重复的话,那么当数组排序之后,数字i 将出现在下标为i 的位置上,如果数组中有重复,那么有些位置可能存在多个数字,利用这点来进行操作。
首先,重排这个数组,从头到尾进行扫描,当在第i 位时,我们先判断这个数字(m)是不是等于i,如果是,那么扫描下一个数字,如果不是,则拿他和第m个数字进行比较,如果他和第m个数字相等,则找到了一个重复数字(因为同时出现在第i,m位置),如果他和第m个数字不相等,就把他们交换。
# include <iostream>
# include <vector>
# include <memory>
using namespace std;
bool duplicate(int numbers[], int length, int* duplication){
if(numbers == nullptr || length <= 1 ) return false; // 等于1就是没重复
for(int i=0;i<length;++i)
{
while(numbers[i] !=i)
{
if(numbers[i] == numbers[numbers[i]]) // 如果要交换位置的两个数相等,即为重复,否则互换位置。
{
*duplication = numbers[i];
return true;
}
int temp = numbers[i];
numbers[i] = numbers[temp];
numbers[temp] = temp;
}
}
return false;
}
int main(){
int numbers[] = {2,3,1,0,2,5,3};
int *duplication = new int(0);
int length = sizeof(numbers)/sizeof(numbers[0]);
duplicate(numbers,length, duplication);
cout<<*duplication<<endl;
return 0;
}
题目二 不修改数组,找出数组中重复的数字
在一个长度为n+1的数组里的所有数字都在1~n的范围内,所以数组中是少有一个数字是重复的。请找出数组中任意一个重复的数字,但不能修改输入的数组。例如输入长度为8的数组{2,3,5,4,3,2,6,7} 那么输出的重复数字是2或者3.
题目二看起来和题目一非常相似,但是不同的是,题目二中
明确规定不能修改输入数组,所以上面提到的交换法就不能再用。
方法1 建立辅助数组
和上面的哈希表类似,只要逐一的复制原数组的每个数字复制到辅助数组(如果被复制的数字是m,则把它复制到辅助数组中下表为m的位置),这样就很容易的发现那个数字是重复的。但是该方法需要O(n)的辅助空间。
方法2 类二分查找
主要思想:
把1~n的数字 从中间的数字m分为两部分,前面那一半为1~m, 后面一半为m+1~n。如果前一半(1-m)的数字总数超过了 m,那么这一半的区间里一定包含了重复的数字,否则,重复数字就在另一半的区间中。然后我们继续对包含重复数字的区间一分为二,直到找到这个重复数字,其实这个方法和二分查找类似,只是多了一步统计区间内数字个数。
# include <iostream>
# include <vector>
# include <memory>
using namespace std;
int getDuplication(const int *numbers, int length) {
if (numbers == nullptr || length <= 0) return -1;
int start = 1, end = length - 1;
while (start < end) {
int lowCnt = 0;
int mid = start + ((end - start) >> 1);
for (int i = 0; i != length; i++) {
if (numbers[i] <= mid && numbers[i] >= start) {
lowCnt++;
}
}
if (lowCnt > (mid - start + 1)) {
end = mid;
}
else {
start = mid + 1;
}
}
return start;
}
int main(){
int numbers[] = {2,4,1,0,3,5,3};
int length = sizeof(numbers)/sizeof(numbers[0]);
int Duplication = getDuplication(numbers,length);
cout<<"The Duplication is : "<<Duplication;
return 0;
}