[JavaScript 刷题] 二分搜索 - 求开方,Leetcode 69
Posted GoldenaArcher
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[JavaScript 刷题] 二分搜索 - 求开方,Leetcode 69相关的知识,希望对你有一定的参考价值。
[javascript 刷题] 二分搜索 - 求开方,Leetcode 69
题目地址:69. Sqrt(x)
题目
如下:
Given a non-negative integer
x
, compute and return the square root ofx
.Since the return type is an integer, the decimal digits are truncated, and only the integer part of the result is returned.
Note: You are not allowed to use any built-in exponent function or operator, such as
pow(x, 0.5)
orx ** 0.5
.
解题思路
依旧日常开始捋题,这道题不算长,而且重点要么加粗了,要么就用斜线标明了,题目含义还算清晰。
-
首先提供的数字,也就是 input
x
是一个非负整数 -
所需的返回值是
x
的平方根,即,当输入值为4
时,返回值是2
-
小数点后的数字会被 压缩,只有 整数 部分的结果会被返回
这一部分显然是描述了 非完全平方数 的平方根的返回值。以 8 为例, 8 \\sqrt{8} 8 的值是 2.82842712... 2.82842712... 2.82842712...,这种情况下小数点后的数字可以省略,只需要返回 2 2 2 即可。
-
不能使用 JavaScript 内置的函数或操作符,例如说
Math.pow()
, 或num ** 0.5
-
根据题目类型提示,这道题可以用 二分搜索 解决
使用 JavaScript 解题
这道题还是挺简单的,直接写就好了
最初的解法
/**
* @param {number} x
* @return {number}
*/
var mySqrt = function (x) {
let lo = 0,
hi = x;
// 将 mid 放在外面,可以在循环体内少创建一些变量
let mid;
while (lo <= hi) {
// JavaScript 的数字类型是 Number,而 Number 不分 整型 和 浮点,所以需要向下取整
mid = Math.floor((lo + hi) / 2);
// 可以不设置变量,这里设置只是为了方便点
const squaredVal = mid * mid;
if (squaredVal === x) return mid;
else if (squaredVal > x) {
hi = mid - 1;
} else {
lo = mid + 1;
}
}
// 注意这个时候的 hi < lo
// 因为循环条件是 lo <= hi
// 当跳出循环了,就代表 hi < lo,否则会一直在循环体内循环
return hi;
};
但是这个时候我发现运行时间大概在 128-148 ms 之间,只超过了 10-20% 左右的成绩。之前也曾经提过,大概是在 50-60% 证明这个算法是比较正常(不搞极端的边界条件)的解,那么只超过 10-20% 就证明还有能够提高的地方。
简单的优化
在不瞎折腾边界条件的情况下,比较能够节省运行时间的方式就是设置上限了,最初的上限是设置成了 hi = x;
,这一点可以进行一些优化。
从图像上来说, y = x 2 y = x^2 y=x2 与 y = 2 x y = 2x y=2x 的交点在 x = 2 x = 2 x=2 上,随后 y = x 2 y = x^2 y=x2 的增长速度就远远的超过了 y = 2 x y = 2x y=2x,于是,这也可以将检查条件分为以下几种情况:
-
当 x ≤ 0 x \\leq 0 x≤0,直接返回 error
题目中提到的是
Given a non-negative integer
,所以在负值的情况下,输入值就是有问题的值,这是比较基本的检查条件 -
当 ( x 2 ) 2 = x (\\frac{x}{2})^2 = x (2x)2=x 时,直接返回 x 2 \\frac{x}{2} 2x
这种情况其实也只存在于 0 和 2,如图所示,这是两个函数的交点
-
其他情况下,将
hi
的值设为 x 2 + 1 \\frac{x}{2} + 1 2x+1之所以设置为 x 2 + 1 \\frac{x}{2} + 1 2x+1 而不是 x 2 \\frac{x}{2} 2x 是因为
mid
会向下取整,直接设置 x 2 \\frac{x}{2} 2x 会导致当输入值为 3 的时候计算错误这一步就相当于直接减少了一次折半的操作,理论上来说是能够稍微提升一点速度的。
JavaScript 的实现为:
/**
* @param {number} x
* @return {number}
*/
var mySqrt = function (x) {
if (x < 0) return new Error('x cannot be smaller than 0');
if ((x / 2) * (x / 2) === x) return x / 2;
let lo = 1,
hi = x / 2 + 1;
while (lo <= hi) {
const mid = Math.floor((lo + hi) / 2);
const squaredVal = mid * mid;
if (squaredVal === x) return mid;
else if (squaredVal > x) {
hi = mid - 1;
} else {
lo = mid + 1;
}
}
// hi is smaller than lo
return hi;
};
跟其他几次对比起来,的确有着比较显著的提速效果:
当然,本质上来说时间复杂度还是 O ( log ( n ) ) O(\\log(n)) O(log(n)),这点并不会有特别大的差别,只是成绩看起来稍微没有这么惨,感觉会好一点……
以上是关于[JavaScript 刷题] 二分搜索 - 求开方,Leetcode 69的主要内容,如果未能解决你的问题,请参考以下文章
[JavaScript 刷题] 二分搜索 - 求开方,Leetcode 69
[JavaScript 刷题] 二分搜索 - 第一个错误的版本,Leetcode 278
[JavaScript 刷题] 二分搜索 - 大于给定元素的最小元素,Leetcode 744