【LeetCode-查找】寻找重复数

题目描述

给定一个包含 n + 1 个整数的数组 nums,其数字都在 1 到 n 之间(包括 1 和 n),可知至少存在一个重复的整数。假设只有一个重复的整数,找出这个重复的数。
示例:

输入: [1,3,4,2,2]
输出: 2

输入: [3,1,3,4,2]
输出: 3

说明:

  • 不能更改原数组(假设数组是只读的)。
  • 只能使用额外的 O(1) 的空间。
  • 时间复杂度小于 O(n2) 。
  • 数组中只有一个重复的数字,但它可能不止重复出现一次。

题目描述: https://leetcode-cn.com/problems/find-the-duplicate-number/

思路

题目中的要求:

  • 不能更改原数组(假设数组是只读的),说明不能用排序的方法;
  • 只能使用额外的 O(1) 的空间, 说明不能使用哈希表;
  • 时间复杂度小于 O(n2),说明不能用两层循环;

可以使用二分查找的思想来做。首先介绍一下鸽巢原理:n+1 只鸽子放到 n 个鸽巢里,则肯定有一个鸽巢中的鸽子个数大于 1。则我们设置两个指针 left 和 right,计算 mid = (left+right)/2。统计输入数组 nums 中小于等于 mid 的元素个数 cnt,如果 cnt>mid,则说明 [left, mid] 之间的元素个数超过了 mid,则重复的数字一定在 [left, mid] 之间,否则重复的数字在 [mid+1, right] 之间。循环结束时,left 就是重复的元素。代码如下:
写法1:

class Solution {
public:
    int findDuplicate(vector<int>& nums) {
        int left = 1;
        int right = nums.size()-1;
        while(left<right){
            int mid = left+(right-left)/2;
            int cnt = 0;
            for(int num:nums){
                if(num<=mid) cnt++;
            }

            if(cnt>mid){
                right = mid; // 因为是循环条件是left<right,所以这里是 right=mid;
            }else{
                left = mid+1;
            }
        }
        return left;
    }
};

写法2:

class Solution {
public:
    int findDuplicate(vector<int>& nums) {
        int left = 1;
        int right = nums.size()-1;
        while(left<=right){
            int mid = left+(right-left)/2;
            int cnt = 0;
            for(int num:nums){
                if(num<=mid) cnt++;
            }

            if(cnt>mid){
                right = mid-1;
            }else{
                left = mid+1;
            }
        }
        return left;
    }
};

写法 1 和写法 2 的区别在于更改了循环条件和 right 的更新方式,这一点和二分查找是一样的。

  • 时间复杂度:O(nlogn)
  • 空间复杂度:O(1)

参考

https://leetcode-cn.com/problems/find-the-duplicate-number/solution/er-fen-fa-si-lu-ji-dai-ma-python-by-liweiwei1419/

posted @ 2020-05-26 20:46  Flix  阅读(639)  评论(0编辑  收藏  举报