面试题3:数组中重复的数字

一、题目

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

二、解法

2.1 方法一

    先把输入的数组排序,从排序的数组中找出重复的数字是一件很容易的事情,只需要从头到尾扫描排序数组后的数组就可以了,排序一个长度为n的数组需要O(nlogn)的时间。下面代码为:先对数组快速排序,然后从头到尾扫描数组,数组中前一个数若和后一个数相同,则将该数组赋值给duplication, 并返回true。

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
    int Partition(int a[], int low, int high)
    {
        int x = a[high];//将输入数组的最后一个数作为主元,用它来对数组进行划分
        int i = low - 1;//i是最后一个小于主元的数的下标
        for (int j = low; j < high; j++)//遍历下标由low到high-1的数
        {    
            if (a[j] < x)//如果数小于主元的话就将i向前挪动一个位置,并且交换j和i所分别指向的数
            {
                int temp;
                i++;
                temp = a[i];
                a[i] = a[j];
                a[j] = temp;
            }
        }
        //经历上面的循环之后下标为从low到i(包括i)的数就均为小于x的数了,现在将主元和i+1位置上面的数进行交换
        a[high] = a[i + 1];
        a[i + 1] = x;
        return i + 1;
    }
    void QuickSort(int a[], int low, int high)
    {
        if (low < high)
        {
            int q = Partition(a, low, high);
            QuickSort(a, low, q - 1);
            QuickSort(a, q + 1, high);
        }
    }
    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-1)
                return false;
        }
        QuickSort(numbers, 0, length-1);
        for(int i=0; i<length-1; ++i)
        {
            if (numbers[i] == numbers[i+1])
            {
                *duplication = numbers[i];
                return true;
            }
        }
        return false;
    }
};
2.2 方法二: hash table

      利用哈希表来解决这个问题,从头到尾按照顺序扫描数组的每个数字,没扫描到一个数字的时候,都可以用O(1)的时间来判断哈希表里是否包含了该数字,如果哈希表还没有这个数字,就把它加入哈希表,如果哈希表里已经存在了该数字,就找到了该数字,这个算法的时间复杂度为O(n),但是它提高时间效率是以一个大小为O(n)的哈希表为代价的。

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-1)
                return false;
        }
        map<int, int> map1;
        for(int i=0; i<length; ++i)
        {
            if (map1.find(numbers[i])!=map1.end())
            {
                map1[numbers[i]]++;
            }
            else
                map1[numbers[i]]=0;
        }
        for(int i=0; i<length; i++)
        {
            if(map1[numbers[i]]>0){
                duplication[0] = numbers[i];
                return true;
            }
        }
        return false;
    }
}; 
2.3 方法三

    数组中的数字都在0~n-1的范围内,如果数组中没有重复的数字,那么数组排序之后数字i将出现在下表为i的位置,由于数组中有重复的数字,有的位置可能存在多个数字,同时有的位置可能没有数字。

    我们重排这个数组,从头到尾依次扫描这个数组中的每个数字,当扫描到下表为i的数字(用m表示)时,首先比较这个数字是不是等于i。如果是,则接着扫描下一个数字;如果不是,则再拿它和第m个数字进行比较,如果它和第m个数字相等,就找到了一个重复的数字(该数字在下表i和m的位置都出现了);如果它和第m个数字不相等,就把第i个数字和第m个数字交换,把m放到属于它的位置,接下来再重复这个比较、交换过程,知道我们发现了一个重复的数字。

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-1)
                return false;
        }
       for(int i=0; i<length; ++i)
       {
           while(i != numbers[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;
    }
};

代码中尽管有一个两重循环,但每个数字最多只要交换两次就能找到属于它自己的位置,因此总的时间复杂度为O(n)。另外,所有的操作步骤都是在输入数组上进行的, 不需要额外分配内存,因此时间复杂度为O(1)。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值