算法---LeetCode 287. 寻找重复数

1. 题目

原题链接

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

示例 1:

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

示例 2:

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

示例 3:

输入:nums = [1,1]
输出:1

示例 4:

输入:nums = [1,1,2]
输出:1

提示:

2 <= n <= 3 * 104
nums.length == n + 1
1 <= nums[i] <= n
nums 中 只有一个整数 出现 两次或多次 ,其余整数均只出现 一次

进阶:

如何证明 nums 中至少存在一个重复的数字?
你可以在不修改数组 nums 的情况下解决这个问题吗?
你可以只用常量级 O(1) 的额外空间解决这个问题吗?
你可以设计一个时间复杂度小于 O(n2) 的解决方案吗?

Related Topics 数组 双指针 二分查找
1052 0

2. 题解

2.1 解法1: 使用哈希表

    class Solution {
        public int findDuplicate(int[] nums) {
            Set<Integer> set = new HashSet<>();
            for (int i = 0; i < nums.length; i++) {
                if (set.contains(nums[i])) {
                    return nums[i];
                }
                set.add(nums[i]);
            }
            return 0;
        }
    }

2.2 解法2: 二分法

分查找的思路是先猜一个数(有效范围 [left…right] 里位于中间的数 mid),然后统计原始数组中 小于等于 mid 的元素的个数 cnt:

  • 如果 cnt 严格大于 mid。根据抽屉原理,重复元素就在区间 [left…mid] 里;
  • 否则,重复元素就在区间 [mid + 1…right] 里。
    class Solution {
        public int findDuplicate(int[] nums) {
            int left = 1, right = nums.length - 1;
            while (left < right) {
                int count = 0;
                int mid = (left + right) / 2;
                for (int num : nums) {
                    if (num <= mid) {
                        count++;
                    }
                }
                // 小于等于 4 的个数如果严格大于 4 个
                // 此时重复元素一定出现在 [1, 4] 区间里
                if (count > mid) {
                    right = mid;
                } else {
                    left = mid + 1;
                }
            }
            return left;
        }
    }

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

2.3 解法3: 快慢指针

类似 算法—LeetCode 142. 环形链表 II, 相当于寻找环的入口,
因为数组中元素值的范围都在 1-n 内, 所以可以把数组当成对链表的一种描述, 数组里的每一个元素的值表示链表的下一个节点的索引, 如示例1中的[1, 3, 4, 2, 2]

把数组索引为0的元素当成链表的头节点
索引为0的元素的值为1, 表示头节点的下一个节点的索引为1, 即数组中的3
再下一个节点的索引为3, 即为第一个2
再下一个节点的索引为2, 即为4
再下一个节点的索引为4, 即为第二个2
再下一个节点的索引为2, 即为4
此时形成了一个环

这里 fast=fast.next.next 相当于 fast = nums[nums[fast]] 向前推进两步

    class Solution {
        public int findDuplicate(int[] nums) {
            int fast = 0, slow = 0;
            while (true) {
                fast = nums[nums[fast]];
                slow = nums[slow];
                if (fast == slow) break;
            }
            slow = 0;
            while (fast != slow) {
                fast = nums[fast];
                slow = nums[slow];
            }
            return slow;
        }
    }

参考: 287.寻找重复数 前端灵魂画师图解快慢指针

你可能感兴趣的:(算法,数组/栈/队列,算法,leetcode)