面试题3.1 :数组中重复的数字
题目描述:
在一个长度为n的数组里的所有数字都在0到n-1的范围内。数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任意一个重复的数字。例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是第一个重复的数字2。
方案1:利用哈希思想解决问题,使用bool数组result保存其数字是否在数组中出现,如果出现标记为true,反之为false;并且其中下标表示的是数字0到n-1;按顺序依次扫描该数组,查看数字i对应的result[i]是否为true,若为true则表示该数字出现过;若为false则将其标记为true。
注意:存在出现多种为false的情况,要考虑完善。
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 (numbers == nullptr || length <= 0){
return false;
}
for (int i = 0; i<length; i++){
if (numbers[i] <0 || numbers[i] >= length){
return false;
}
}
vector<bool> flag;
for (int i = 0; i < length; i++){
flag.push_back(false);
}
for (int i = 0; i < length; i++){
if (flag[numbers[i]] == true){ //表示含有该元素
*duplication = numbers[i];
return true;
}
//如果没有该元素
flag[numbers[i]] = true;
}
return false;
}
};
时间复杂度:O(n)
空间复杂度:O(n)
方案2:注意到数组里的所有数字都在0到n-1的范围内,如果这个数组中没有重复的数字,那么当数组排序之后数字i将出现在下标为i的位置。由于数组中含有重复的数字,因此有的位置可能存在多个数字,有的位置没有数字。
根据上述我们可以重排这个数组,如下表格所示。
数组 | 比较过程 | 结果 |
2,3,1,0,2,5,3 | 位置0≠2 ; 2≠1 | 2 <-> 1 (2和1交换位置) |
1,3,2,0,2,5,3 | 位置0≠1 ; 1≠3 | 1 <-> 3 |
3,1,2,0,2,5,3 | 位置0≠3 ; 3≠0 | 3 <-> 0 |
0,1,2,3,2,5,3 | 位置0=0 | 位置+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(numbers == nullptr || length<=0){
return false;
}
for(int i=0;i<length;i++){
if(numbers[i] <0 || numbers[i] >=length){
return false;
}
}
for(int i=0;i<length;i++){
#注意交换的顺序以及是while循环
while(numbers[i] != i){
if(numbers[i] != numbers[numbers[i]]){
int temp = numbers[numbers[i]];
numbers[numbers[i]] = numbers[i];
numbers[i] = temp;
}
else{
*duplication = numbers[i];
return true;
}
}
}
return false;
}
};
时间复杂度:O(n)
空间复杂度:O(1)
测试用例:
- 长度为n的数组里包含一个或多个重复的数字;
- 数组里不包含重复的数字;
- 无效输入测试用例(数组长度为0;数组包含>=n的数字等)。
参考链接:
https://blog.csdn.net/ddkxddkx/article/details/6555754 (C++ STL中的hash_map和map)
https://blog.csdn.net/u013164931/article/details/79741700
https://www.jianshu.com/p/e081904d2a68
《剑指offer》P39
面试题3.2 :不修改数组找出重复的数字
题目描述:
在一个长度为n+1的数组里的所有数字都在1到n的范围内,所以数组中至少有一个数字是重复的。请找出数组中任意一个重复的数字,但不能修改输入的数组。例如,如果输入长度为8的数组{2,3,5,4,3,2,6,7},那么对应的输出是重复的数字2或3。
方案1:参考3.1中的方案1
方案2:由于方案1中的空间复杂度为O(n),因此尝试避免使用O(n)的辅助空间。
假如数组中没有重复的数字,那么从1到n的范围里只有n个数字。但由于数组长度超过n,说明肯定包含重复数字,因此在某范围里的数字的个数可以用来解决这个问题。
根据上述,我们可以举例表示,如下表格所示,假定原始数组为{2,3,5,4,3,2,6,7}。
Start | End (最初为数组长度-1) | Middle ((Start+End)/2) | Start<=元素<=Middle的个数 (标记为A) | Start==End && A>1 (当为True时,说明该数字为重复的数字,算法结束) | Middle-Start +1 (标记为B) | A>B(若为True,则说明Start~Middle中存在重复的数字;反之Middle~End中存在重复的数字) |
1 | 7 | 4 | 5 | False | 4 | True |
1 | 4 | 2 | 2 | False | 2 | False |
2 | 2 | 2 | 2 | True |
|
|
int countRange(const int* numbers,int length,int start,int end){
if (numbers == NULL){
return -1;
}
int count = 0;
for (int i = 0; i < length; i++){
if (numbers[i] >= start && numbers[i] <= end){
count++;
}
}
return count;
}
int getDuplication(const int* numbers, int length){
if (numbers == NULL || length <= 0){
return -1;
}
for (int i = 0; i < length; i++){
if (numbers[i] < 1 || numbers[i] >= length){
return -2;
}
}
int start = 1;
int end = length - 1;
while (end >= start){
int middle = (start + end) / 2; //int middle = ((end -start)>>1) + start;
int count = countRange(numbers,length,start,middle);
if (start == end){
if (count > 1){
return start;
}
else{
break;
}
}
if (count > (middle -start +1)){
end = middle;
}
else{
start = middle+1;
}
}
return -3;
}
时间复杂度:O(nlog(n))
空间复杂度:O(1)
注意:1.方案2与方案1相比,是以时间换取空间;
2.代码中数组的length可以使用sizeof(numbers)/sizeof(int)计算得到。
测试用例:
- 长度为n+1的数组里包含一个或多个重复的数字,数组里不可能没有重复的数字;
- 数组里的一个数字重复多次;
- 无效输入测试用例(数组长度为0;数组包含>=n的数字等)。
总结:
在解决数组中重复数字的问题时,应根据不同的功能要求(找出任意一个重复的数字、找出所有重复的数字)或者性能要求(时间效率优先、空间效率优先),选择不同的算法。动手写代码之前一定要弄清楚题目或面试官的需求。
参考链接:
《剑指offer》P41
https://github.com/zhedahht/CodingInterviewChinese2/blob/master/03_02_DuplicationInArrayNoEdit