当前位置:   article > 正文

leetcode *287. 寻找重复数(2020.5.26)_寻找重复数 桶排序

寻找重复数 桶排序

【题目】*287. 寻找重复数

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

示例 1:

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

示例 2:

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

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

【解题思路1】排序

class Solution {
    public int findDuplicate(int[] nums) {
        Arrays.sort(nums);
        for (int i = 1; i < nums.length; i++) {
            if (nums[i] == nums[i-1]) {
                return nums[i];
            }
        }

        return -1;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

【解题思路2】哈西排重

class Solution {
    public int findDuplicate(int[] nums) {
        Set<Integer> seen = new HashSet<Integer>();
        for (int num : nums) {
            if (seen.contains(num)) {
                return num;
            }
            seen.add(num);
        }

        return -1;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

【解题思路3】快慢指针

复制一段评论里对官方题解对方法三的说明:
一般而言,面试官会更注重基础、常见的知识的考察。我个人觉得按照 :1、哈希表判重,2、再说排序以后相邻元素相等即找到的重复;3、桶排序;4、二分法。然后分析一下这些方法的时间复杂度、空间复杂度,做一个简单比较,我觉得应该就很不错了。我是不会讲方法三的,感觉面试的时候说方法三是给自己挖坑。

此方法是将nums的value作为下一个要去的下标值
先用快慢指针找到相交点,然后其中一个重新指向首节点,两个指针同时走,再次相遇时即为循环首节点
tortoise和hare不一定会在重复数字处相遇,比如[2,5,9,6,9,3,8,9,7,1]
或许可以再优化一下,如果相遇的时候正好index也不同,就直接是重复元素了

class Solution {
    public int findDuplicate(int[] nums) {
        // Find the intersection point of the two runners.
        int tortoise = nums[0];
        int hare = nums[0];
        do {
            tortoise = nums[tortoise];
            hare = nums[nums[hare]];
        } while (tortoise != hare);

        // Find the "entrance" to the cycle.
        int ptr1 = nums[0];
        int ptr2 = tortoise;
        while (ptr1 != ptr2) {
            ptr1 = nums[ptr1];
            ptr2 = nums[ptr2];
        }

        return ptr1;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
class Solution {
    public int findDuplicate(int[] nums) {
        // Find the intersection point of the two runners.
        int tortoise = nums[nums[0]];
        int hare = nums[nums[nums[0]]];
        while (tortoise != hare) {
            tortoise = nums[tortoise];
            hare = nums[nums[hare]];
        }

        // Find the "entrance" to the cycle.
        int ptr1 = nums[0];
        int ptr2 = tortoise;
        while (ptr1 != ptr2) {
            ptr1 = nums[ptr1];
            ptr2 = nums[ptr2];
        }

        return ptr1;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

【解题思路4】二分查找

以 [1, 2, 2, 3, 4, 5, 6, 7] 为例,一共 8 个数,n + 1 = 8,n = 7,根据题目意思,每个数都在 1 和 7 之间。

例如:区间 [1, 7] 的中位数是 4,遍历整个数组,统计小于等于 4 的整数的个数,至多应该为 4 个。换句话说,整个数组里小于等于 4 的整数的个数如果严格大于 4 个,就说明重复的数存在于区间 [1, 4],它的反面是:重复的数存在于区间 [5, 7]。

于是,二分法的思路是先猜一个数(有效范围 [left, right]里的中间数 mid),然后统计原始数组中小于等于这个中间数的元素的个数 cnt,如果 cnt 严格大于 mid,(注意我加了着重号的部分“小于等于”、“严格大于”)依然根据抽屉原理,重复元素就应该在区间 [left, mid] 里。

public class Solution {
    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;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/你好赵伟/article/detail/794992
推荐阅读
相关标签
  

闽ICP备14008679号