69. Sqrt(x) - 力扣(LeetCode)
文章更新:2021年10月16日15:59:14
问题描述及示例
给你一个非负整数 x ,计算并返回 x 的 算术平方根 。
由于返回类型是整数,结果只保留 整数部分 ,小数部分将被 舍去 。
注意:不允许使用任何内置指数函数和算符,例如 pow(x, 0.5) 或者 x ** 0.5 。
示例 1:
输入:x = 4
输出:2
示例 2:
输入:x = 8
输出:2
解释:8 的算术平方根是 2.82842…, 由于返回类型是整数,小数部分将被舍去。
提示:
0 <= x <= 231 - 1
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/sqrtx
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
我的题解
成功前的尝试
第一想法就是从 1
开始找到 x
的所有因数,然后根据找到的最后一对因数来决定返回结果。
/**
* @param {number} x
* @return {number}
*/
var mySqrt = function(x) {
// fac用于存储一对对的因数
let fac = [];
// 从1开始找x的因数
let n = 1;
// 注意成对的因数都是各自往中间逼近的
while(n <= x / n) {
// 如果当前数字是x的因数
if(!(x % n)) {
// 则将当前数字以及与其配套的另一个因数放入fac中
fac.push(n, x / n);
}
// 如果当前数字不是x的因数,则遍历下一个
n++;
}
// 如果x是0,则返回0,如果不是的话就返回最后一对因数的平均值
return x ? Math.floor((fac[fac.length-2] + fac[fac.length-1]) / 2) : 0;
};
提交记录
执行结果:解答错误
通过测试用例:12 / 1017
输入:5
输出:1
预期结果:2
时间:2021/10/16 14:03
这个解法的错误就在于没有考虑到 x
的平方根可能离最后找到的那对因数都有一定的距离,不一定就是两者的平均值。比如 x = 37
时,其平方根结果是 6
,但是其最后一对因数是 1
和 37
,两者去除小数部分的平均值为 19
。
我的题解1(暴力解法)
这种方法和上面的尝试有相似之处。也是遍历的思路。关键的命中条件判断逻辑就是如果 x
在 n2 和 (n+1)2 之间,那么 x
的平方根结果去除小数部分就是 n
。
/**
* @param {number} x
* @return {number}
*/
var mySqrt = function(x) {
// 如果x是0,则直接返回0
if(!x) {
return 0;
}
// 从1开始遍历,
let n = 1;
// 如果当前数字的值小于等于另一个因数(虽然该因数可能不是整数)
while(n <= x / n) {
// 如果当前数字的平方小于等于x,且下一个数字的平方大于x,
// 说明当前数字就是我们要找的平方数,直接将其返回
if(n * n <= x && x < (n + 1) * (n + 1)) {
return n;
}
// 否则的话就遍历下一个数字
n++;
}
};
提交记录
1017 / 1017 个通过测试用例
状态:通过
执行用时:180 ms, 在所有 JavaScript 提交中击败了5.29%的用户
内存消耗:39.2 MB, 在所有 JavaScript 提交中击败了69.55%的用户
时间:2021/10/16 14:46
可以看到上面这种解法的性能还是比较低的。因为这相当于是要遍历一些不必要的元素。所以下面的二分法就显得更高效一点。
我的题解2(二分查找)
更新:2021年10月16日15:57:37
小憩一会儿,回来继续~
其实本题也可以用二分查找的思路来寻找目标值,因为查找过程中所涉及到的数字都是有序的。有关二分查找的思路,可以看下方博客:
本题的思路和上面的二分查找是一样的,也是利用元素大小有序的特点进行对半地筛选工作。而本题就相当于是在 1 ~ x
这些数中寻找符合条件的值。
/**
* @param {number} x
* @return {number}
*/
var mySqrt = function (x) {
// 如果x是0或1,直接返回x本身即可
if (x === 0 || x === 1) {
return x;
}
// 初始化左右指针分别指向待查区间的头部和尾部,这里left初始化为0也可以
let left = 1;
let right = x;
// mid指针用于指示区间的中点,初始值是什么都无所谓,因为后面会被覆盖
let mid = 0;
// 开始搜寻,直到区间长度小于0
while (left <= right) {
// 计算当前区间的中点位置,为了防止溢出,这里的计算不是直接采用(left+right)/2
mid = Math.floor(left + (right - left) / 2);
// 如果当前中点的值的平方比x大,则让right指针移到mid前一位,
// 这里也是为了防止溢出,所以不是直接采用 mid * mid
if (mid > x / mid) {
right = mid - 1;
} else if (mid < x / mid) {
// 如果当前中点的值的平方比x小,则让left指针移到mid后一位,
left = mid + 1;
} else {
// 如果当前中点的值的平方恰好是x则直接返回结果
return mid;
}
}
// 如果上面没有搜寻到合适的值,则此时left和right指针一定停留在目标值的附近,
// 而且此时的left指针一定是在right指针的后一位,而因为题目要求去除结果的小数部分
// 所以我们就可以去较小的right值作为结果
return right;
};
提交记录
1017 / 1017 个通过测试用例
状态:通过
执行用时:80 ms, 在所有 JavaScript 提交中击败了83.33%的用户
内存消耗:39.4 MB, 在所有 JavaScript 提交中击败了31.60%的用户
时间:2021/10/16 15:54
官方题解
更新:2021年7月29日18:43:21
因为我考虑到著作权归属问题,所以【官方题解】部分我不再粘贴具体的代码了,可到下方的链接中查看。
更新:2021年10月16日14:48:51
【更新结束】
有关参考
更新:2021年10月16日15:57:22
参考:【算法-LeetCode】704. 二分查找_赖念安的博客-CSDN博客