javascript 之二分查找

大家好,我是 17

写二分的时候,不要每次都重新写,而是记住一个模板,每次都用这个模板。这样做的好处是不容易出错,而且速度快,特别适合面试。

function binarySearch(nums, target) {
   let low=0,high=nums.length
   while(low<high){
       let mid=low+((high-low)>>1)
       if(nums[mid]>=target) high=mid
       else low=mid+1
   }
   return low
};
  
输入输出
[ 1,2,2,4 ] , 21
[ 1,2,2,4 ] , 44
[ 1,2,2,4 ] , 33
[ 1,2,2,4 ] , 00

把模板当作一个黑盒,熟记什么样的输入会有什么样的输出。

如果 nums 是升序

  1. 如果 target 存在,一定会找到,如果有相同值,命中最左边的位置
  2. 如果不存在,会命中 比 target 略大的值
  3. 如果 target 大于所有值,会超出最右边界 low == nums.length
  4. 如果 target 小于所胡值, low == 0

总的来说,就两条,如果 target 存在,命中最左置,如果不存在,命中偏右位置(如果 target 存在,相比于 target的位置)

(high-low)>>1 等价于 Math.floor((high-low)/2),如果你不习惯于移位的写法,也可以这么写。

要这么写low+ Math.floor((high-low)/2)而不是 Math.floor((high+low)/2) 是因为 high+low可能会溢出,超出 数字的最大表示范围。

[low,high) 左闭右开,也就是说,high的位置比可以取到的位置 加 1,一定要记住。

熟记上面的模板后,我们先练练手。

704 二分查找

给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。

解题:这是最简单的二分查找题目,直接用模板即可。如果目标值存在,则一定能找到。

var search = function(nums, target) {
   let low=0,high=nums.length
   while(low<high){
       let mid=low+((high-low)>>1)
       if(nums[mid]>=target) high=mid
       else low=mid+1
   }
   return nums[low]===target?nums[low]:-1
};
  

35. 搜索插入位置

给定一个按升序排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。

解题:根据模板特性 第1条,目标值存在则一定找到,并可以返回索引。如果不存在,根据模板特性 第2条 ,返回 比 target 略大的值,正好是插入的位置。代码和模板一模一样。

var searchInsert = function(nums, target) {
   let low=0,high=nums.length
   while(low<high){
       let mid=low+((high-low)>>1)
       if(nums[mid]>=target) high=mid
       else low=mid+1
   }
   return low
};

367. 有效的完全平方数

给定一个 正整数 num ,编写一个函数,如果 num 是一个完全平方数,则返回 true ,否则返回 false

解题:可以认为是 从 大于等于1 到 小于等于 num 的数中,看看能不能选出一个数 x x * x = num
如果存在,根据 模板特性1 一定会找到,如果不存在 ,根据特性2 ,会命中一个稍大的数。根据左闭右开的原则,low = 1 ,1是可以取到的最小值,high=num+1 num是可以取到的最大值

var isPerfectSquare = function (num) {
  let low = 1, high = num + 1
  while (low < high) {
    let mid = low + ((high - low) >> 1)
    if (mid * mid >= num) high = mid
    else low = mid + 1
  }
  return low * low == num
};

接下来,自己到 leetcode 上面找几道简单题练习一下,切记用模板,只能修改两个地方,其它地方都不要修改。

nums[mid]>=target 这个判断是可以修改的,这也查为什么二分查找不适合成一个通用方法的原因

return low 返回值这里 根据题意 灵活修改。

如果 nums里面有重复数,模板是命中最左边的位置 ,如果想要命中最右边的位置

nums[mid]>target
...
return low-1

如果你都顺利通过了,说明你已经掌握了模板,可以尝试些复杂些的题目了。这些题目的关键在于如何找到二分的条件。

153. 寻找旋转排序数组中的最小值

题解:最小值一定在两段有序数组的交界偏右的位置。如果中值比nums[0]小,说明交界一定在左侧,否则在右侧。
根据 模板 特性1 如果 交界 存在,则一定找到 。如果交界不存在,也就是 数组整个有序,那么 nums[mid]<nums[0] 永远为假,high 最后的结果 就是 nums.length,low 的退出条件是 等于 high,low == nums.length

var findMin = function(nums) {
   let low=0,high=nums.length
   while(low<high){
       let mid=low+((high-low)>>1)
       if(nums[mid]<nums[0]) high=mid
       else low=mid+1
   }
   return nums[low]?? nums[0]
};

34. 在排序数组中查找元素的第一个和最后一个位置

题解:用模板可以找到最左面的位置,最右边的位置可以用 前面讲到的

nums[mid]>target
...
return low-1

var searchRange = function(nums, target) {
    let low=0,high=nums.length;
    let ans=[]
    while(low<high){
        let mid=low+((high-low)>>1)
        if(nums[mid]>=target) high=mid
        else low=mid+1
    }
    
    if(nums[low]!==target) return [-1,-1]
    ans[0]=low
    high=nums.length
    while(low<high){
         let mid=low+((high-low)>>1)
         if(nums[mid]>target) high=mid
        else low=mid+1
    }
    ans[1]=low-1
    return ans
};

把上面提到的都熟记后,找几道题练练,二分的基本功就差不多了。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

IAM17前端

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值