二分法

231 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第6天,点击查看活动详情

二分法

二分法我们应该都很熟悉了,它的主要原理就是对一堆数据以中间值为分割点,分为左右两部分,再与目标进行对比,舍弃掉明显不符合的那部分,从而不断来缩小检索范围,直到找到或接近想要的结果。

但要使用二分法,需要满足一定的条件:

  1. 存在上下边界:不存在上下边界,中间值也无从谈起
  2. 单调递增或递减:如果是杂乱无章的数据,取中间值也就没有意义
  3. 能够通过索引访问:整个过程需要不断寻找中间值

x的平方根

描述

计算一个非负整数的平方根,计算记过舍弃小数,仅保留整数。如:

输入:x = 4
输出:2

输入:8
输出:2 // 8的平方根约等于2.8284271247461903,取整数部分2

分析

首先,当然不允许使用语言的内置函数(像Math.sqrt(x))来执行。

先做个简单过滤,如果x的值是0和1,则直接返回x;

我们可以考虑用二分法来不断的逼近这个值,初始边界值取[0, x],然后不断通过中间值的平方和目标值x进行比较:

  • 如果mid²等于x,则返回中间值
  • 如果mid²小于x,则左边界值设置为mid,说明结果值要大于mid,继续循环比较
  • 如果mid²大于x,则右边界值设置为mid,说明结果值要小于mid,继续循环比较

代码

var mySqrt = function(x) {
  if (x === 0 || x === 1) return x
  let left = 0
  let right = x
  while (right - left > 1) {
    let mid = Math.floor((left + right) / 2)
    const midSqu = mid * mid
    if (midSqu === x) {
      return mid
    } else if (midSqu < x) {
      left = mid
    } else {
      right = mid
    }
  }
  return left
};

这里有个地方需要注意:取中间值mid,如果直接用(left + right)/2,可能会不断产生小数。因为在本题中我们仅取整,并且是向下取整,那我们在获取mid值的时候,就直接通过Math.floor(mid)向下取整。