参考代码:
class Solution {
public int findDuplicate(int[] nums) {
int len = nums.length;
int left = 1;
int right = len - 1;
while (left < right) {
int mid = left + (right - left) / 2;
int cnt = 0;
for (int num : nums) {
if (num <= mid) {
cnt += 1;
}
}
if (cnt > mid) {
right = mid;
} else {
left = mid + 1;
}
}
return left;
}
}
大致思路:二分法。
先猜一个数(有效范围 [left..right] 里位于中间的数 mid),然后统计原始数组中 小于等于 mid 的元素的个数 cnt:
如果 cnt 严格大于 mid。根据抽屉原理,重复元素就在区间 [left..mid] 里;
否则,重复元素就在区间 [mid + 1..right] 里。
具体实现:
int left = 1;
int right = len - 1;
确定最大的一个区间[left,right]即[1,n]
然后找到最中间的一个值,注意将区间一分为二后还要加上区间的下限,这样才是区间的中间的数。遍历mid左边的数,统计小于等于mid的个数
if (cnt > mid) {
right = mid;//重复元素位于区间 [left..mid]
} else {
left = mid + 1;//else 搜索的区间就是 if 的反面区间 [mid + 1..right]
}
小于等于 4 的个数如果严格大于 4 个,此时重复元素一定出现在 [1..4] 区间里.
循环到最后会是left=right,也就是那个重复的数。