287. Find the Duplicate Number

在这里插入图片描述

这题很有意思,居然可以用老朋友-二分。
1.
首先忽略要求的不可以改变数组的限制,可以这么做:

class Solution {
public:
    int findDuplicate(vector<int>& nums) {
        int sz = nums.size();
        while (nums[nums[0]] != nums[0]) 
            swap(nums[nums[0]], nums[0]);
        return nums[0];
    }
};

因为数字从1-n,没有0,所以位置0放的一定是重复的数字。

2.当成有环的链表

class Solution {
public:
    int findDuplicate(vector<int>& nums) {
        int fast = nums[nums[0]], slow = nums[0];//都是从0开始,不过需要避免第一次while判断失败
        while (fast != slow) {
            slow = nums[slow];
            fast = nums[nums[fast]];
        }
        fast = 0;
        while (fast != slow) {
            slow = nums[slow];
            fast = nums[fast];
        }
        return fast;
    }
};

3.二分
按照之前总结的两步步来试试二分:
1.有明确的上下界:1-n
2.对于某个判断,一边可以,另一边不可以:关键就是找出这个判断条件,而这不太容易看出来。
这一题的判断是:对于所有大于等于目标数字的数字a,数组中小于等于它的数字个数要>a;对于小于目标数字的数字b,数组中小于等于它的数字个数要<=b。
为什么:比如说,只重复了两次,比如1,2,3,3,4.很明显可以看出是这样。但如果大于两个,比如1,3,3,3,4,或者1,2,3,3,3,看出依然这样。

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

所以说,以后只要最终值有明确的上下界,就尝试着找一找是不是对于某个判断,一边可以,另一边不可以,这个判读往往不太好找,需要好好想一想。但如果找出来,二分往往很好。

posted @ 2019-09-11 10:21  于老师的父亲王老爷子  阅读(19)  评论(0)    收藏  举报