《剑指offer》学习笔记:数组中的重复数字

目录

题目描述

方法一

方法二

方法三

测试代码

  • 题目描述

    在一个长度为n的数组里的所有数字都在0~n-1的范围内。数组中某些数字重复的,但不知道有几个重复的数字,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。例如:如果输入长度为7的数组{2,3,1,0,,2,5,3},那么对应的输出是重复的数字2或者3.

  • 方法一

  • 思路

    由于数组中的元素在数组下标范围内,因此如果没有重复的话,对其排序时,每个元素所在的位置是提取知道的,例如3就是数组A[3]的位置上。因此可以通过交换在O(n)时间内对其进行排序,如果在交换过程中遇到重复的数字,那么就找到了我们要的答案,否则没有。

  • C++实现
/*******************************************************************

Function:duplicate_1

Input:

    numbers:数组

    length;数组numbers的长度

    duplication:用于保存重复的数字

********************************************************************/

bool duplicate_1(int numbers[],int length,int* duplication){

    if(NULL==numbers||length<=1)

        return false;

    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){

            //检查将要执行交换的两个元素,若相同则直接返回

            if(numbers[i]==numbers[numbers[i]]){

                *duplication = numbers[i];

                return true;

            }

            swap(numbers[i],numbers[numbers[i]]);

        }

    }

    return false;

}

 

  • 方法二

  • 思路

    在原始的数组不可以被修改的条件下,可以使用类似于方法一的方法,但是新申请一个数组,然后将数组重排中新数组中。此时的时间复杂度为O(n),空间复杂度也为O(n).

  • C++实现
bool duplicate_2(const int numbers[],int length,int* duplication){

    if(NULL==numbers||length<=1)

        return false;

    for(int i=0;i<length;i++){

        if(numbers[i]<0||numbers[i]>length-1)

            return false;

    }

    int* a = new int[length];

    for(int i=0;i<length;i++)

        a[i] = -1;

    for(int i=0;i<length;i++){

        if(a[numbers[i]]==numbers[i]){

            *duplication = numbers[i];

            return true;

        }

        a[numbers[i]] = numbers[i];

    }

    delete[] a;

    return false;

}

 

  • 方法三

  • 思路

    在原始数组不可以被修改的情况下,类似于折半查找,例如数组长度为n,即元素介于0到n-1,先统计数组中的元素介于0到n/2的元素的个数,如果个数大于n/2+1,说明存在重复的元素。然后再考察0到n/4,依次类推。当区间收缩到单个元素,而且该元素的个数大于1,就得到了重复的数字。折半查找的时间复杂度为O(logn),统计区间内元素的个数的时间复杂度O(n),故总的时间复杂度为O(nlogn).

  • C++实现
 /*******************************************************************

Function:countRange

Description:统计数组numbers中在区间[l,h]中元素的个数

********************************************************************/

int countRange(const int numbers[],int length,int l,int h){

    if(NULL==numbers)

        return 0;



    int count = 0;

    for(int i=0;i<length;i++){

        if(numbers[i]>=l&&numbers[i]<=h)

            count++;

    }

    return count;

}

bool duplicate_3(const int numbers[],int length,int* duplication){

    if(NULL==numbers||length<=1)

        return false;

    for(int i=0;i<length;i++){

        if(numbers[i]<0||numbers[i]>length-1)

            return false;

    }

    int l = 0,h=length;

    while(l<=h){

        int mid = (l+h)/2;

        int count = countRange(numbers,length,l,mid);

        //收缩到单个数字且这个数组在numbers中不止一个

        if(l==h&&count>1){

            *duplication = numbers[l];

            return true;

        }

        if(count>(mid-l+1))

            h = mid;

        else

            l = mid+1;

    }

    return false;

}

 

  • 测试代码


 int main(){

    int A[] = {3,1,2,0,2,5,3};//测试用例1

    //int* A = NULL;//测试用例2:空指针

    //int A[] = {6,3,4,5,2,1,0};//测试用例3:无重复元素

    int* duplication = new int;

    if(duplicate_3(A,sizeof(A)/sizeof(int),duplication)){

        cout<<*duplication;

    }else{

        cout<<"no duplication";

    }

    delete duplication;

}

 

点赞