[LeetCode] 287. Find the Duplicate Number 解题思路

Given an array nums containing n + 1 integers where each integer is between 1 and n (inclusive), prove that at least one duplicate number must exist. Assume that there is only one duplicate number, find the duplicate one.

Note:

  1. You must not modify the array (assume the array is read only).
  2. You must use only constant, O(1) extra space.
  3. Your runtime complexity should be less than O(n*n).
  4. There is only one duplicate number in the array, but it could be repeated more than once.

问题:给定一个长度为 n+1 的数组,里面只有 1...n 的数值。可见必然有一个元素的值是重复出现的。假设只有最多只有一个元素的值会重复出现,求出重复的那个值。

特别要求:

  1. 不能修改原数组

  2. 只用固定空间,空间使用为 O(1)

  3. 时间复杂度要小于 O(n*2)

基于前两个要求,我首先想到的是两个指针,逐个遍历求解,但是耗时 O(n*n),超时了。在网上看到其他算法,能满足题目要求。

设 l 为左值, r 为右值, mid 为两者的中间值。值得注意的是,这里的 l, r, mid 均是指元素的值,不是指下标,之所以可以这么做,是因为题目的条件“ n+1 长的数组里面只有 1...n 的数值”。

  • 将数组扫一遍,得到大于等于 l 且 小于等于 mid 的元素个数,即为 num_l_mid。
  • 当 num_l_mid 大于 本应该有的个数,则将重复值定位在 [l, mid] 之间,缩小范围。
  • 当 num_l_mid 小于等于 本应该有的个数,则将重复值定位在 [mid, r] 之间,缩小范围。
  • 重复前面步骤,直到找到重复值。
int findDuplicate(vector<int>& nums) {
    
    if (nums.size() == 2) {
        return 1;
    }

    int l = 1;
    int r = (int)nums.size()-1;
    int mid = (r + l) / 2 ;
    
    int cnt = 0;
    while (l < r) {
        
        int leftLen = mid - l + 1;
        
        for (int i = 0 ; i < nums.size(); i++) {
            if ( l <= nums[i] && nums[i] <= mid) {
                cnt++;
            }
        }if (l+1 == r) {
            if (cnt > leftLen) {
                return l;
            }else{
                return r;
            }
        }
        
        
        if (cnt > leftLen) {
            r = mid;
            mid = (l + mid) / 2;
        }else{
            l = mid;
            mid = (mid + r) / 2;
        }
        
        mid = (r + l) / 2 ;
        cnt = 0;
    }

    return 0;
}
原文地址:https://www.cnblogs.com/TonyYPZhang/p/5061078.html