不修改数组找出重复的数字
给定一个长度为 n+1
的数组nums,数组中所有的数均在 1∼n
的范围内,其中 n≥1。
请找出数组中任意一个重复的数,但不能修改输入的数组。
数据范围
1≤n≤1000
样例
给定 nums = [2, 3, 5, 4, 3, 2, 6, 7]。
返回 2 或 3。
思考题:如果只能使用 O(1) 的额外空间,该怎么做呢?
思路:
抽屉原理 一共有n+1个苹果放到n个抽屉里面 那么一定至少有一个抽屉里面存放了 两只苹果
二分法思路: 每次分开半 遍历左边半的数据范围 l 到 mid 查看全部数字 在左边的范围的数字是否大于 范围本身(mid - l+1) 如果是 说明左边存在重复元素 不是的话 则是右边
直到l==r 为止
代码
class Solution {
public:
int duplicateInArray(vector<int>& nums) {
// l 和 r 分别代表的是 数字 1 和 数字n 这里并不是下标.
int l = 1, r = nums.size() - 1;
while (l < r){
// 二分 找到中间的那个数
int mid = l + r >> 1;
int s = 0;
// 下面这句话的意思 从 nums里面 循环去先去 判断 这个数 x 的值看他是否在 [l, mid]中间, 在的话 判断条件执行完为true
// true 的话代表 数字 1 flase 代表 数字 0. 然后再进行累加 s += x 统计符合条件的个数.
// 最终的效果就是 统计了 整个数组中 数的值 在 [l,mid] 之间的个数.
for (auto x : nums) s += x >= l && x <= mid; // left : [l, mid] , right : [mid + 1, r]
// 理解: 一个坑存一个数, 正常情况下 一定是坑的个数 和 数的个数相等. 如果一坑里面有两个数. 那么就会出现
// 数的个数 大于 坑的个数 说明 这个区间段一定存在重复的个数.
if (s > mid - l + 1) r = mid;
else l = mid + 1;
}
return r;
}
};