LeetCode 69. Sqrt(x)

题目

Implement int sqrt(int x).

Compute and return the square root of x, where x is guaranteed to be a non-negative integer.

Since the return type is an integer, the decimal digits are truncated and only the integer part of the result is returned.

Example 1:

Input: 4
Output: 2

Example 2:

Input: 8
Output: 2
Explanation: The square root of 8 is 2.82842..., and since 
             the decimal part is truncated, 2 is returned.

求一个数的平方主要有两种方法:二分法和牛顿法。

1. 二分法

比如给定的数是x,那么我们从x/2开始试,判断它的平方是大于x还是小于x,如果大于那么就向下折半继续查找,如果小于就向上折半继续查找,直到左右相等的时候停止。思路很简单,代码写起来倒有不少讲究,时间复杂度O(logn):

class Solution {
public:
    int mySqrt(int x) {
        int l = 1, r = x;
        while (l <= r) {
            int mid = (l + r) / 2;
            if (mid > x / mid) {
                r = mid - 1;
            }
            else {
                l = mid + 1;
            }
        }
        return r;
    }
};

这个代码其实纠结了我挺久的,也是参考了discussion里大佬们的代码才写出来。自己主要卡的地方在于:

(1)左边从0开始,其实应该从1开始就行。另外据评论区有人说,右边从x/2开始就可以了,但好像没见过这样的代码……

(2)其实我对二分法的停止条件一直很迷茫,不知道什么时候该用<=、什么时候该用<,以及里面的if也是不知道怎么处理相等的情况,还有最后返回应该返回left还是right还是mid……看到这样一个解释,但也还是没能完全理解为什么这样就是正确的:

(3)防止溢出的小技巧:<1>在取mid的时候最好mid = left + (right - left) / 2,防止left + right产生溢出;<2>不要使用mid * mid > x,而采取mid > x / mid,防止mid * mid溢出。

2. 牛顿法

牛顿法非常巧妙,我也是看了好几篇文章(但每篇都没全部看完)然后大概弄懂了它的意思。首先是它的原理的直观解释,参考:https://www.zhihu.com/question/20690553/answer/146104283 的前三部分可以得到一个宏观上的理解,说明为什么可以通过切线来不断逼近曲线的根,后面几部分感觉涉及到数学方面的知识就比较多了,遂放弃。理解这个以后,我们得出了迭代公式:x_{n+1}=x_n-\frac{f(x_n)}{f'(x_n)}。接下来,由于求平方根的话相当于求f(x) = x^2-a的根,因此f'(x)=2x,代入前面的迭代公式可以得到求平方根的迭代公式为:x_{n+1}=x_n-\frac{x_n^2-a}{2x_n}=\frac{1}{2}(x_n+\frac{a}{x_n})。另外一个我觉得非常绝妙的解释是这个:https://www.zhihu.com/question/20690553/answer/543620219,把求平方根比作把一个面积为A、长为x的长方形逐渐变成正方形。

于是我们就可以快乐地写代码了:

class Solution {
public:
    int mySqrt(int x) {
        long x0 = x;
        while (x0 * x0 > x) {
            x0 = (x0 + x / x0) / 2;
        }
        return floor(x0);
    }
};

这代码看起来简单,实际上我提交了好几遍没跑过test case的submission……第一次我while里面写的是x0 > x / x0,想着可以防止溢出,结果在跑0的时候光荣runtime error……第二次改正了前面这个毛病,然后就暴露了我x0采用double产生的错误,不知道为什么,x=5的时候会TLE,非常神秘,我怀疑是有什么神秘精度问题,sigh。第三次想试试int行不行,发现跑5没问题了就开心地提交了,结果在x=2147395599的时候应该是光荣溢出了……卒。最后只好改用long,嗯,以后遇到这种数字可能巨大的问题,还是用long比较保险呢。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值