1、题目描述
2、解题思路
本题限制了不能改变原数组,且智能使用额外的 O(1) 的空间,说白了就是不能把无序数组改为有序。
数组 nums 包含了 1 到 n 之间的数字,因此大概为:[1,2,3…,target,…n] 的样子。
定义一个变量 cnt[i] 表示 nums[] 数组中小于等于 i 的数有多少个。
例如数组 [1,3,4,2,2] 的 cnt = {0,1,3,4,5}。
可以得出一个规律:在[0, target-1] 区间内,cnt[i] ≤ i;在 [target, n] 区间内,cnt[i] > i。
因此,采用二分的方法,在 1 到 n 之间找出一个 target,使得它是最小的满足 cnt[i] > i 的数。
具体算法如下:
1、用二分法在[1,2,3…,target,…n]中选择一个候选值mid,
2、若mid处 cnt <= mid,则说明target在mid右边:[1,2,3…mid…target…n]
3、反之,target在mid左边:[1,2,3…target…mid…n]
4、按上述判断结果重新划定[left,right],得出新的候选值mid,继续判断
3、解题代码
class Solution {
public int findDuplicate(int[] nums) {
int left = 1;
int right = nums.length - 1;
int ans = -1;
while (left <= right) {
int mid = left + (right - left) / 2;
int cnt = 0; // cnt 表示 nums[] 数组中小于等于 mid 的数有多少个
for (int i = 0; i < nums.length; ++i) {
if (nums[i] <= mid) {
cnt++;
}
}
if (cnt <= mid) {
left = mid + 1;
} else {
right = mid - 1;
ans = mid;
}
}
return ans;
}
}