LeetCode/solutions/287. Find the Duplicate Number.md
2019-11-07 19:02:02 +08:00

3.9 KiB
Raw Blame History

287. Find the Duplicate Number

思路

给定一个长度为n+1的数组里面所有的元素都位于[1, n],所以很明显有重复元素,题目假设只有一个重复数字(但可以重复多次),求这个重复数字。要求空间复杂度为常数 且时间复杂度小于O(n^2)。

思路一、位操作

由于要求常数空间复杂度所以就不能用hash什么的然后要求时间复杂度小于O(n^2),暴力计数也不行。既然直接计数不行,那么我们可以考虑位操作: 计算32位中每一位当中1出现的次数。具体来讲对于第i位0 <= i <= 31我们用count1代表整个nums数组中所有元素第i位是1的个数 用count2代表0~n这些数中第i位是1的个数。若 count1 > count2说明最终所求的重复元素在第i位是1否则是0。这样我们就可以通过分别计算32位1出现的次数来得到结果。

时间复杂度为O(n)

思路二、二分法

题目说的时间复杂度要小于O(n^2)那么我们可以想到比这个复杂度小一个量级的常见复杂度O(nlogn),由此可想到二分法。 由于重复值肯定是在[1, n]之间所以我们可以首先得到这个范围的中点mid然后遍历整个数组统计所有小于等于mid的数的个数count

  • 如果count <= mid则说明重复值一定是大于mid可以自己举例子理解应进入右半部分继续二分
  • 否则重复值不大于mid。

此时时间复杂度就是O(nlogn)

思路三

此题还有一个十分巧妙的方法[可参考此处]。此时要把nums数组看成一个链表nums[i] = j表示链表中结点i的next结点为j例如数组

index  : 0 1 2 3 4 
nums[i]: 3 1 3 4 2

表示的链表为

0 -> 3 -> 4 -> 2
     ^         |
     |_________|

可以看到这个链表是有环的为什么有环呢就是因为数组nums中存在重复元素3。仔细分析我们可以发现链表中环的开始结点即为重复元素 即题目转变为求一个带环链表中环的开始结点,这其实就是142. Linked List Cycle II,可参见这题我的题解,这里直接给出代码。

C++

思路一

class Solution {
public:
    int findDuplicate(vector<int>& nums) {
        int res = 0, mask = 1, n = nums.size() - 1;
        
        for(int i = 0; i < 32; i++){
            int count1 = 0, count2 = 0;
            for(int j = 0; j <= n; j++){
                if(mask & nums[j]) count1++;
                if(mask & j) count2++;
            }
            if(count1 > count2) res += mask;
            if(i < 31) mask <<= 1;
        }
        return res;
    }
};

思路二

class Solution {
public:
    int findDuplicate(vector<int>& nums) {
        int n = nums.size() - 1;
        
        int low = 1, high = n;
        while(low < high){
            int mid = low + (high - low) / 2;
            int count = 0;
            
            for(auto &num: nums)
                if(num <= mid) count++;
            
            if(count <= mid) low = mid + 1;
            else high = mid;
        }
        return low;
    }
};

思路三

class Solution {
public:
    int findDuplicate(vector<int>& nums) {
        int slow = 0, fast = 0;
        while(true){
            slow = nums[slow];
            fast = nums[nums[fast]];
            if(slow == fast) break;
        }
        
        int p = 0;
        while(true){
            if(p == slow) break;
            slow = nums[slow];
            p = nums[p];
        }
        return slow;
    }
};