一、题目说明
69. x 的平方根 - 力扣(LeetCode) (leetcode-cn.com)
给你一个非负整数 x ,计算并返回 x 的 算术平方根 。
由于返回类型是整数,结果只保留 整数部分 ,小数部分将被 舍去 。
注意:不允许使用任何内置指数函数和算符,例如 pow(x, 0.5) 或者 x ** 0.5 。
二、思路及题解
首先这道题主要需要避免的问题就是整数溢出以及超出时间限制的问题。
思路一:
用最简单暴力的解法,题目只要求输出开平方后所得的整数部分,小数部分直接舍去,也不考虑四舍五入的情况,可以用i * i <= x
来判断,最后返回i - 1
即可,i
必须用long
或者double
类型,否则会造成整数溢出。
这样的解法时间复杂度太高了,不可取,可以用二分法进行改进。
代码:
class Solution {
public:
int mySqrt(int x) {
long i = 0;
while (i * i <= x) i += 1;
return i - 1;
}
};
思路二:
用二分法的时间,降低时间复杂度,并且用x / mid
的方式解决整数溢出的问题。
下面给出二分法的三种写法,前两种写法主要的区别就是循环中left
和right
的等号判断问题。
代码写法一:
class Solution {
public:
int mySqrt(int x) {
if (x <= 1) return x;
int left = 0;
int right = x;
// 这一行代码的意思实际上就是(left+right)/2
int mid = left + (right - left) / 2;
while (left <= right){
if (mid > x / mid) right = mid - 1;
else left = mid + 1;
mid = left + (right - left) / 2;
}
return mid - 1;
}
};
代码写法二:
class Solution {
public:
int mySqrt(int x) {
if (x <= 1) return x;
int left = 0;
int right = x;
int mid = left + ((right - left) / 2) + 1;
while (left < right){
// 这一行代码的意思实际上就是(left+right)/2 + 1
if (mid > x / mid) right = mid - 1;
else left = mid;
mid = left + ((right - left) / 2) + 1;
}
return mid - 1;
}
};
代码写法三:
使用1为精度,同样利用x/mid
解决整数溢出问题
class Solution {
public:
int mySqrt(int x) {
// 二分法
int left = 0, right = x;
int mid = (left + right) / 2;
if (x == 1) return x;
while (right - left > 1){
if (mid > x / mid) right = mid;
else left = mid;
mid = (right + left) / 2;
}
return mid;
}
};
思路三:
利用牛顿迭代法的方式,注意r
的类型是long
class Solution {
public:
int mySqrt(int x) {
if (x <= 1) return x;
long val = x;
while (val > x / val){
val = (val + x / val) / 2;
}
return int(val);
}
};