287.寻找重复的数

image-20200527122935680

思路

  • 本题设置的条件很特殊 ,解法反常规,用时间换空间
  • 我也只能想到哈希解法 该解法还不符合要求2
  • 参考各种题解 收集以下解法(不局限于题设要求)

哈希

/**
 * 使用HashMap   2ms  但不能满足 要求2
 */
public static int findDuplicate(int[] nums) {
    int ans=0;
    Map<Integer,Integer> map=new HashMap<>();
    for (int num : nums) {
        if (map.containsKey(num)) {
            return num;
        } else {
            map.put(num, 1);
        }
    }
    return ans

更改原数组

/**
 * 2ms  但不满足要求 1
 *  
 * 非重复的数字只会改一次正数变成负数,之后就不会访问了,
 * 重复的数字会在第二次索引看到之前变为负数的值
 */
public int findDuplicate3(int[] nums){
    for(int i=0;i<nums.length;i++){
        if(nums[Math.abs(nums[i])]<0) return Math.abs(nums[i]);
        nums[Math.abs(nums[i])]=-nums[Math.abs(nums[i])];
    }
    return -1;
}

二分法(抽屉原理)

原文思路参考:

liweiwei1419:使用二分法查找一个有范围的整数(结合抽屉原理)

/*
 * 2ms 
 * 
 */
public int findDuplicate(int[] nums) {
    int len = nums.length;
    int left = 1;
    int right = len - 1;
    while (left < right) {
        // 在 Java 里可以这么用,当 left + right 溢出的时候,无符号右移保证结果依然正确
        int mid = (left + right + 1) >>> 1;

        int cnt = 0;
        for (int num : nums) {
            if (num < mid) {
                cnt += 1;
            }
        }

        // 根据抽屉原理,严格小于 4 的数的个数如果大于等于 4 个,
        // 此时重复元素一定出现在 [1, 3] 区间里

        if (cnt >= mid) {
            // 重复的元素一定出现在 [left, mid - 1] 区间里
            right = mid - 1;
        } else {
            // if 分析正确了以后,else 搜索的区间就是 if 的反面
            // [mid, right]
            // 注意:此时需要调整中位数的取法为上取整
            left = mid;
        }
    }
    return left;
}
  • 时间复杂度:O(N log N),二分法的时间复杂度为 O(logN),在二分法的内部,执 行了一次 for 循环,时间复杂度为 O(N),故时间复杂度为 O(N logN)。
  • 空间复杂度:O(1)

快慢指针

题解参考

官方题解

jsl:前端灵魂画师图解快慢指针

/*
 *0ms 
 *时间复杂度O(n)
 *空间复杂度O(1)
 */
class Solution {
    public int findDuplicate(int[] nums) {
        int slow = 0, fast = 0;
        do {
            slow = nums[slow];
            fast = nums[nums[fast]];
        } while (slow != fast);
        slow = 0;
        while (slow != fast) {
            slow = nums[slow];
            fast = nums[fast];
        }
        return slow;
    }
}

题解参考

明知山有虎?:287.寻找重复数

class Solution {
    public int findDuplicate(int[] nums) {

        if(nums.length <= 2){
            return nums[0];
        }
        //初始化i,j不应该为0.应该是指向第一个index与nums[index]不相等的位置.
        int i = 0,j = 0;
        for(int index = 0; index<nums.length; index++){
            if(index != nums[index]){
                i = index;
                j = index;
                break;
            }
        }
        while(true){
            i = nums[i];
            j = nums[nums[j]];
            if(i == j) break;
        }
        i = 0;
        while(true){
            i = nums[i];
            j = nums[j];
            if(i == j) break;
        }
        return i;
    }
}

小结

  • 第一次了解抽屉原理,惊叹二分法的新用法,当然题解还需好好理解。
posted @ 2020-05-30 20:51  YH_Simon  阅读(123)  评论(0编辑  收藏  举报