AcWing 14 不修改数组找出重复的数字

题目描述:

给定一个长度为 n+1 的数组nums,数组中所有的数均在 1∼n 的范围内,其中 n≥1。

请找出数组中任意一个重复的数,但不能修改输入的数组。

样例:

给定 nums = [2, 3, 5, 4, 3, 2, 6, 7]。

返回 2 或 3。

分析:

本题与上题相比有些变化,一方面是数的个数与范围改变了,另一方面要求不能改变输入的数组,当然我们可以备份一下原数组,然后再调用上一题的思路,这样做时间复杂度是O(n),空间复杂度为O(n)。

如果要求就地算法,便不能在线性的时间复杂度内解决该问题了。

思路:

1.n+1个数,放在1-n内,必定有一个格子内有重复的数字(抽屉原理)。

2.我们采用二分的办法,逐个遍历数组,同时统计数字大小不大于mid的数,如果统计的数字大于mid,则往左边继续二分,否则往右边继续二分,直到区间长度为1终止。此时mid左右两边的数字差不多相等,则mid即是所求的。

下面解释下代码:

1.这里的二分不是对数组下标,而是对元素的值,l从1开始,r从n开始。

2.s += x >= l && x <= mid;这行语句表示的意思是x在l到mid之间时,s++,否则s不改变。

3.注意决定二分方向的判断,不是简单的s > mid,而是s > mid - l + 1,这在二分类排序算法里是易错点。

4.本算法可以继续剪枝,不必遍历完nums再判断s,可以在for循环里面判断,一旦s超过了mid - l + 1,立刻break,继续二分。

class Solution {
public:
    int duplicateInArray(vector<int>& nums) {
        int l = 1,r = nums.size() - 1;
        while(l < r){
            int mid = l + r >> 1;
            int s = 0;
            for(auto x : nums)  s += x >= l && x <= mid;
            if(s > mid - l + 1) r = mid;
            else    l = mid + 1;
        }
        return r;
    }
};


 

猜你喜欢

转载自blog.csdn.net/qq_30277239/article/details/88069083