鸽巢原理(寻找重复数题解)

169 阅读2分钟

力扣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 字(可选)