287. 寻找重复数
寻找重复数
给定一个包含 n + 1
个整数的数组 nums
,其数字都在 [1, n]
范围内(包括 1
和 n
),可知至少存在一个重复的整数。
假设 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, n]
上进行二分查找,检查数字的分布情况。根据每次二分的结果,可以不断缩小搜索的范围,直到找到重复的数字。
详细步骤:
- 二分查找:我们定义一个区间
[low, high]
,初始化为[1, n]
,然后在这个区间内进行二分查找。 - 计数:对于区间
[low, high]
,我们计算数组中有多少个数字小于或等于mid
(即mid = (low + high) / 2
)。如果这些数字的个数超过了mid - low + 1
,则说明重复的数字在[low, mid]
这个区间内。 - 缩小范围:根据计数的结果,调整
low
和high
的值,不断缩小搜索范围,直到找到重复的数字。
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 即为重复的数字
}
}
解释:
low
和high
:初始化为[1, n]
,这表示数组中的元素在这个范围内。mid
:在[low, high]
范围内选择中间值mid
,然后统计数组中小于等于mid
的元素个数。- 计数:通过遍历数组,统计数组中小于等于
mid
的元素个数。若该个数大于mid
,则说明重复的元素必然在[low, mid]
范围内。 - 更新区间:根据统计结果,更新
low
和high
,直到low == high
,此时low
就是重复的数字。
细节解释
- 如果
count > mid
:- 这意味着在数组中,值小于或等于
mid
的元素有 超过mid
个。由于数组中的元素范围是[1, n]
,而数组长度是n + 1
,这就意味着某个数字至少重复一次,重复的数字必须在low
到mid
这个区间内。 - 因此我们将
high
设置为mid
,缩小查找范围到左半区间。这个时候我们并不加1
,因为我们还需要检查当前mid
是否为重复元素。
- 这意味着在数组中,值小于或等于
- 如果
count <= mid
:- 这意味着在数组中,值小于或等于
mid
的元素的个数不超过mid
。这表明重复的数字可能在mid + 1
到high
这个区间内,因为mid
之前的数字分布没有问题。 - 因此,我们将
low
设置为mid + 1
,缩小查找范围到右半区间。
- 这意味着在数组中,值小于或等于
再次强调二分查找的工作区间是 [left,right] 的闭区间。
本文作者:Drunker•L
本文链接:https://www.cnblogs.com/drunkerl/p/18650924
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步