从何时你也忌讳空山无人,从何时开始|

Drunker•L

园龄:4个月粉丝:0关注:0

287. 寻找重复数

寻找重复数
给定一个包含 n + 1 个整数的数组 nums ,其数字都在 [1, n] 范围内(包括 1n),可知至少存在一个重复的整数。

假设 nums 只有 一个重复的整数 ,返回 这个重复的数

你设计的解决方案必须 不修改 数组 nums 且只用常量级 O(1) 的额外空间。

示例 1:

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

示例 2:

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

示例 3 :

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

思路

这个问题可以通过 二分查找 来解决,我们可以利用数组中数字的分布来推测重复的数字。

二分法的核心思想

  1. 分段统计法:我们把数组中的数字分成两部分:一部分小于等于某个值,另一部分大于这个值。通过计数的方式,我们可以判断重复的数字在哪一段。
  2. 二分查找的条件:通过在区间 [1, n] 上进行二分查找,检查数字的分布情况。根据每次二分的结果,可以不断缩小搜索的范围,直到找到重复的数字。

详细步骤:

  1. 二分查找:我们定义一个区间 [low, high],初始化为 [1, n],然后在这个区间内进行二分查找。
  2. 计数:对于区间 [low, high],我们计算数组中有多少个数字小于或等于 mid(即 mid = (low + high) / 2)。如果这些数字的个数超过了 mid - low + 1,则说明重复的数字在 [low, mid] 这个区间内。
  3. 缩小范围:根据计数的结果,调整 lowhigh 的值,不断缩小搜索范围,直到找到重复的数字。
public class Solution {
    public int findDuplicate(int[] nums) {
        int low = 1, high = nums.length - 1;  // 数字范围是 [1, n]
        
        while (low < high) {
            int mid = low + (high - low) / 2;  // 防止溢出
            int count = 0;
            
            // 统计 nums 中小于等于 mid 的数字个数
            for (int num : nums) {
                if (num <= mid) {
                    count++;
                }
            }
            
            // 如果小于等于 mid 的数字个数大于 mid,那么重复的数字在 [low, mid] 之间
            if (count > mid) {
                high = mid;  // 继续在左半区间查找
            } else {
                low = mid + 1;  // 继续在右半区间查找
            }
        }
        
        return low;  // low 即为重复的数字
    }
}

解释:

  1. lowhigh:初始化为 [1, n],这表示数组中的元素在这个范围内。
  2. mid:在 [low, high] 范围内选择中间值 mid,然后统计数组中小于等于 mid 的元素个数。
  3. 计数:通过遍历数组,统计数组中小于等于 mid 的元素个数。若该个数大于 mid,则说明重复的元素必然在 [low, mid] 范围内。
  4. 更新区间:根据统计结果,更新 lowhigh,直到 low == high,此时 low 就是重复的数字。

细节解释

  1. 如果 count > mid
    • 这意味着在数组中,值小于或等于 mid 的元素有 超过 mid。由于数组中的元素范围是 [1, n],而数组长度是 n + 1,这就意味着某个数字至少重复一次,重复的数字必须在 lowmid 这个区间内。
    • 因此我们将 high 设置为 mid,缩小查找范围到左半区间。这个时候我们并不加 1,因为我们还需要检查当前 mid 是否为重复元素。
  2. 如果 count <= mid
    • 这意味着在数组中,值小于或等于 mid 的元素的个数不超过 mid。这表明重复的数字可能在 mid + 1high 这个区间内,因为 mid 之前的数字分布没有问题。
    • 因此,我们将 low 设置为 mid + 1,缩小查找范围到右半区间。

再次强调二分查找的工作区间是 [left,right] 的闭区间。

本文作者:Drunker•L

本文链接:https://www.cnblogs.com/drunkerl/p/18650924

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   Drunker•L  阅读(11)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起