寻找重复数(力扣第287题)

287.寻找重复数

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

示例 1:

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

示例 2:

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

说明:

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

分析:

​ 该题要求相当苛刻,数组是只读的,不能使用额外空间,那么借助哈希表的方法也就不行了,时间复杂度则要求不能使用暴力法,数组中只有一个重复的数字,但是这个重复出现的数字可能不止重复一次。

​ 一开始做起这个题也没有头绪,所以也参考了其他人的解法,主要使用的是二分法和快慢指针。

​ (1)二分法

​ 这个数组乍一看,并不是一个有序数组,并不能直接使用二分法,但是经过一些处理,就能造出一个升序数组出来。首先明确一点,该数组一共有n+1个元素,这些元素值都在1-n之间,那么我们可以定义一个数组count[i],用于记录给定数组中小于等于i的元素个数,其中1=<i<=n。同时定义一个变量target,表示数组中重复的元素。

​ 对于原数组中在[1,targrt-1]区间内的元素来说,其满足 count[i] <=i;

​ 对于原数组中在[target,n]区间内的元素来说,其满足 count[i] > i;

​ 解释一下为啥是count[i] <= i,如果原数组中重复的元素只出现两次,因为原数组的大小是n+1,那么1-n这个范围内的所有元素都会出现,所以此时count[i] ==i;如果原数组中重复出现的元素出现的次数大于两次,那么1-n这个范围内有的元素并不会出现,不会出现的元素可被认为由重复元素替代了,对于不会出现的元素分为两种情况:

​ (1)没出现的元素i位于[1,targrt-1]内,那么[i+1,targrt-1]区间内的元素k对应的count值应该都减去1,此时就是count[k] < k了;

​ (2)没出现的元素j位于[target,n]内,那么[target,j-1]区间内的元素k对应的count值都应该加上1,此时也满足count[k]>k了;

​ 比如,上面的示例2,[3,1,3,4,2],其count与nums值的对应关系如下:

nums 1 2 3 4
count 1 2 4 5

​ 我们要找的就是上面这个表中,第一个count[i] > i的那个值,这个值就是重复值。

​ 相当于原始数组的元素值作为count值的索引(1-n),然后对count数组进行二分查找,寻找第一个count值大于其索引值的的那个元素,这元素的count索引值就是最终的结果。

实现代码:

public int findDuplicate2(int[] nums) {

    int n = nums.length;
    int left = 1;
    int right = nums.length-1;
    while (left < right){

        int mid = (right + left) / 2;
        int count = 0;

        for (int i = 0; i < n; i++) {
            if (nums[i] <= mid){
                count++;
            }
        }
        if ( count <= mid ){
            left = mid + 1;
        }else {
            right = mid;
        }
    }
    return left;
}

参考文档:

https://github.com/CyC2018/CS-Notes/blob/master/notes/Leetcode 题解 - 数组与矩阵.md#5-有序矩阵的-kth-element

https://leetcode-cn.com/problems/find-the-duplicate-number/solution/kuai-man-zhi-zhen-de-jie-shi-cong-damien_undoxie-d/

原文地址:https://www.cnblogs.com/yxym2016/p/14022664.html