一、题目
给你一个非负整数 x
,计算并返回 x
的 算术平方根 。
由于返回类型是整数,结果只保留 整数部分 ,小数部分将被 舍去 。
注意:不允许使用任何内置指数函数和算符,例如 pow(x, 0.5)
或者 x ** 0.5
。
示例 1:
输入:x = 4 输出:2
示例 2:
输入:x = 8 输出:2 解释:8 的算术平方根是 2.82842..., 由于返回类型是整数,小数部分将被舍去。
二、题解
对于这道题,最开始没什么思路,在查看题解并分析后总结如下:
1、二分查找
思路:二分查找即将n个元素分成两部分,每次将中间值mid的平方与目标值x相比较
1、若mid的平方等于x,mid即为结果,直接返回目mid;
2、若mid的平方大于x,那么x的平方根应该在[left,mid-1]范围内,right=mid-1,在左侧继续进行二分查找;
3、若mid的平方小于x,那么x的平方根应该在[mid+1,right]范围内,left=mid+1,在右侧继续进行二分查找。代码如下:
class Solution {
public:
int mySqrt(int x) {
int left=0,right=x,ans=-1;//左指针,右指针,存放临时结果
while(left<=right){ //left==right时的值是有效的
int mid=left+(right-l)/2;
if((long long)mid*mid<=x){//向右二分
left=mid+1;
ans=mid;
}else{ //向左二分
right=mid-1;
}
}
return ans;
}
};
分析:时间复杂度:O(logx),空间复杂度:O(1)。由于所有运算均是整数运算,不会存在误差。
2、牛顿迭代法:
思路:求算术平方根即令x^2=C,即可以转化为函数f(x)=x^2-C=0求零点问题,从x0开始迭代(以防迭代到负的平方根)。每次过(x0,f(x0))作f(x)的切线,切线与x轴的交点x1比x0更接近零点,此时再过(x1,f(x1))作f(x)的切线,切线与x轴交点x2比x1更接近零点,依次类推,直到迭代的两个值差值小于1e-7表示两个点非常接近,此时可以认为这是算术平方根。计算x0和x1关系如下:
代码如下:
class Solution {
public:
int mySqrt(int x) {
if(x==0) return 0;
double x0=x,c=x;
while(true){
double x1=(x0+c/x0)/2;由切线方程可以得出
if(abs(x0-x1)<1e-7){//确定循环结束条件
break;
}
x0=x1;
}
return (int)x0;
}
};
分析:时间复杂度:O(logx),空间复杂度:O(1)。
3、袖珍计算器算法:
袖珍计算器算法是利用指数函数exp和对数函数ln来代替平方根的方法。即
代码如下:
class Solution {
public:
int mySqrt(int x) {
if(x==0) return 0;
int ans=exp(0.5*log(x));
return ((long long)(ans+1)*(ans+1))<=x?ans+1:ans;//判断误差
}
};
分析:时间复杂度:O(1),空间复杂度:O(1)。由于指数函数和对数函数返回的是浮点数值,故还需要误差判断。
总结:二分排序需要注意循环条件和后续的区间赋值问题。
第三篇博客,完成!