力扣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
题目条件:
数组只读——不能去移动数不能排序
不能新建数组再排序
不能用暴力法
只有一个数字重复,但可能重复多次
什么是鸽巢原理?
假如有n个笼子和n+1只鸽子,所有的鸽子都被关进去了,那么至少有一个笼子里有2只鸽子。 知道他的原理倒是简单,如何去实现呢?这里需要用到反证法 假设每个笼子最多放 1只鸽子,那么n个笼子最多放 n只鸽子 与n+1只鸽子都需要被关在鸽笼里是矛盾的
题解
我们知道了可以用鸽巢原理,针对上面那题如何去理解呢? 题目说明了数组的范围在 1 - n 假设正常升序数组是 [1,2,3,4,5,6,7,8] ;那小于等于任意一个数的都是这个数的本身(啥意思呢? 假设变量m=7时 <=7的一共有7个;m=2时<=2的数一共有2个) 我们再看题目的实例1: nums = [1,3,4,2,2] (m=3时 <=3的有3个没错;但m=2时 就出现问题了!) 让我们看看代码如何实现吧~
/**
* @param {number[]} nums
* @return {number}
*/
var findDuplicate = function(nums) {
let left = 1
let right = nums.length - 1 //设置边界为左闭右闭
while(left <= right){ //最后一次进循环的条件是重合left、right重合
let mid = Math.floor( (left + right) / 2 ) //向下取整 取一个中间数
let count = 0 //设置count进行计数
for(let i = 0; i<nums.length; i++){
if(nums[i] <= mid){
count++
}
}
if(count > mid){
right = mid-1
}else{
left = mid+1
}
}
return left
};
工作过程
mid = (1 + n) / 2,重复数要么落在[1, mid],要么落在[mid + 1, n] 遍历原数组,统计 <= mid 的元素个数,记为 k。 如果k > mid,说明有超过 mid 个数落在[1, mid],但该区间只有 mid 个“坑”,说明重复的数落在[1, mid]。 相反,如果k <= mid,则说明重复数落在[mid + 1, n]。 对重复数所在的区间继续二分,直到区间闭合,重复数就找到了。
添加图片注释,不超过 140 字(可选)
添加图片注释,不超过 140 字(可选)
添加图片注释,不超过 140 字(可选)