剑指offer编程题 --数组中重复的数字

面试题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)

 

 

测试用例:

  1. 长度为n的数组里包含一个或多个重复的数字;
  2. 数组里不包含重复的数字;
  3. 无效输入测试用例(数组长度为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)计算得到。

    

 

测试用例:

  1. 长度为n+1的数组里包含一个或多个重复的数字,数组里不可能没有重复的数字;
  2. 数组里的一个数字重复多次;
  3. 无效输入测试用例(数组长度为0;数组包含>=n的数字等)。

 

 

总结:

在解决数组中重复数字的问题时,应根据不同的功能要求(找出任意一个重复的数字、找出所有重复的数字)或者性能要求(时间效率优先、空间效率优先),选择不同的算法。动手写代码之前一定要弄清楚题目或面试官的需求。

 

 

参考链接:

《剑指offer》P41

https://github.com/zhedahht/CodingInterviewChinese2/blob/master/03_02_DuplicationInArrayNoEdit

 

 

 

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值