題目:
給你一個非負整數 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; } };
兩種做法內存占用相差不大
