题目:
给你一个非负整数 x ,计算并返回 x 的 平方根 。 由于返回类型是整数,结果只保留 整数部分 ,小数部分将被 舍去 。 注意:不允许使用任何内置指数函数和算符,例如 pow(x, 0.5) 或者 x ** 0.5 。 示例 1: 输入:x = 4 输出:2 示例 2: 输入:x = 8 输出:2 解释:8 的平方根是 2.82842..., 由于返回类型是整数,小数部分将被舍去。 提示: 0 <= x <= 231 - 1 链接:https://leetcode-cn.com/problems/sqrtx
第一种思路是,牛顿迭代公式:
假设有一条曲线C,在曲线上面任选一点x0 = 1, 求的曲线的值为f(1), 即(1, f(1))为曲线上得一点。过点(1, f(1)), 作一条曲线C的切线,切线与X轴相交于点x1。同理使用x1求得x2、x3、x4......。所求得的一些列与X轴相交的点位曲线与X轴相交点得近视值。如设定某一误差e,当x(n+1)-x(n) < e,则可认为x(n+1)是曲线的一个近视解。因为x(n+1)作为曲线的解误差为可以接受的e。
其实,对于某个点,相对于曲线的切线方程是确定的,即为:f(x0) = f'(x0)(x - x0), 其中f'(x0)为切线的斜率。化简即为x1 = x0 - f(x0)/f'(x0)。由此可得牛顿迭代公式为:
x(n+1) = x(n) - f(x(n))/f'(x(n));
要求根号2的近似值,可假设f(x) = x^2 - 2 = 0;即曲线x^2 -2 = 0的解即为根号2的值,然后根据上方公式不断逼近解
代码如下,既然返回结果为整数,那么不妨规定误差为e=0.1,代码如下:
class Solution { public: int mySqrt(int x0) { if(x0==0) return 0; double x1=1,x2=0; //初始点选为x=1,当数值很大时,float不够精确, double difference=x1>x2?x1-x2:x2-x1; //计算每次的误差,正值 int res=0; while(difference>0.1) //设定误差小于0.1后得到结果 { x2=x1-(x1*x1-x0)/(2*x1); //根据公式计算下一个点的横坐标x2 difference=x1>x2?x1-x2:x2-x1;//更新误差 x1=x2; //更新公式中最新的横坐标到x1 } res=floor(x2); return res; } };
第二种思路是二分法,left=1,right=x,然后通过mid依次向结果靠近,left与right重合或交叉后停止,即为结果:
class Solution { public: int mySqrt(int x) { if(x==0) return 0; int left=1,right=x; int mid; while(right-left>1) { mid=left+(right-left)/2; if(mid>x/mid) //如果当前mid平方比x大,那么在左区间 right=mid; else //反之,剩余情况认为在右区间 left=mid; } return left; } };
两种做法内存占用相差不大