zoukankan      html  css  js  c++  java
  • JS leetcode x 的平方根 题解分析

    壹 ❀ 引

    这几天心情复杂,也不知道形容。做道题吧,其实是上周的题,一直没整理,比较巧的是,这也是我同学17年去PPTV面试时遇到的一题,题目来自leetcode69. x 的平方根,题目描述如下:

    实现 int sqrt(int x) 函数。

    计算并返回 x 的平方根,其中 x 是非负整数。

    由于返回类型是整数,结果只保留整数的部分,小数部分将被舍去。

    示例 1:

    输入: 4
    输出: 2
    

    示例 2:

    输入: 8
    输出: 2
    

    说明: 8 的平方根是 2.82842...,
    由于返回类型是整数,小数部分将被舍去。

    站在JavaScript角度,其实就是让我们自己实现Math.sqrt方法,题目也不用分析了,唯一需要注意的是,比如8这样的数字平方根为2.82842...,因此取2,而非取3,让我们来实现它。

    贰 ❀ 解题思路

    Math中除了有求平方根的sqrt方法以外,其实还有求幂的pow,因此我们可以利用pow直接解题:

    /**
     * @param {number} x
     * @return {number}
     */
    var mySqrt = function (x) {
        return Math.floor(Math.pow(x, 0.5));
    };
    

    当然,我们也可以使用笨一点的方法,由于0和1的平方根等于自己,所以从2开始,只要当第一个数字的平方比x还大,那么我们返回此数字减一位即可,像这样:

    /**
     * @param {number} x
     * @return {number}
     */
    var mySqrt = function (x) {
        // 考虑0和1
        if(x<2){
            return x;
        };
        // 从2开始查找
        let a = 2;
        while (a * a <= x) {
            a++;
        };
        return a - 1;
    };
    

    很明显,虽然这样做能实现,但是查找起来很慢,如果x的平方根越大,我们需要遇到它耗时就越久。而官方对于此题推荐的做法是使用二分查找。

    举个例子,假设输入的是100,我们要找的就是10,由于上面的答案也分析了,0与1可以直接返回,所以left起点可以从2开始,那么右边边界怎么定呢?其实我们可以将右边起点定为Math.floor(x/2),为啥?0,1不用考虑,紧接着是数字2,2也是唯一一个的2次方后再除以2与自己相等的数,再往上走的任意数字,比如3的2次方为9,9除以2为4.5,显而易见4.5的二次方要比3的二次方大,直接将x除以2求floor的数完全没问题。

    这里有个小技巧,当我们想让9/2为4时,一般的做法是前面说的结合Math.floor,其实还可以使用位运算符,这个跟二进制有关,大家先知道是这么回事就行。

    9>>1 //4
    8>>1 //4
    6>>1 //3
    

    也就是说,将数字除以2,如果能整除得到的就是这个整数,如果不能,则用floor向下取整,比如9>>1就是因为为4.5经过向下取整得到的结果。

    我们先上代码:

    /**
     * @param {number} x
     * @return {number}
     */
    var mySqrt = function (x) {
        if (x < 2) {
            return x
        };
        let left = 2,
            right = x >> 1;//这里等同于Math.floor(x/2)
        while (left <= right) {
            // 同理,这里也是用了位运算符,保证mid是个整数
            let mid = left + ((right - left) >> 1);
            //使用x/mid而非mid*mid是怕数字越界
            if (mid === x / mid) {
                return mid
            } else if (mid < x / mid) {
                left = mid + 1
            } else {
                right = mid - 1
            };
        };
        // 最后直接用right和x/right相比较,如果大肯定取left,反之取right
        return right > x / right ? left : right
    };
    

    唯一需要注意的是,二分法中取mid我在之前的文章中用的是Math.floor((low + high) / 2),但是网友说这样容易越界,也就是假设hight很大了,偏偏加了low已经越界,就无法参与计算了。所以另外一种求中间的做法就是left + Math.floor((right-left)/2),由于使用了位运算,所以就简写成left + ((right - left) >> 1)了,至于后面的判断也就是用x/mid与mid比较的问题,也不用过多解释。

    最后我们总是会得到left与right两个数,再做最后一次比较即可。

    那么本文就到这里了。

  • 相关阅读:
    Linux 间网线直连
    Ubuntu 14.04安装配置NFS
    Android Native IPC 方案支持情况
    使用WindowsAPI获取录音音频
    Ubuntu 64编译32位程序
    TensorFlow 安装 Ubuntu14.04
    纯css实现表单输入验证
    安装ELectron失败解决方案
    正则学习
    常见web攻击
  • 原文地址:https://www.cnblogs.com/echolun/p/13200552.html
Copyright © 2011-2022 走看看