一、题目描述

实现 int sqrt(int x) 函数。

计算并返回 x 的平方根,其中 x 是非负整数。

由于返回类型是整数,结果只保留整数的部分,小数部分将被舍去。

示例 1:

输入: 4
输出: 2

示例 2:

输入: 8
输出: 2
说明: 8 的平方根是 2.82842..., 
     由于返回类型是整数,小数部分将被舍去。

二、解题思路 & 代码

2.1 二分法

**思路分析:**使用二分法搜索平方根的思想很简单,就类似于小时候我们看的电视节目中的“猜价格”游戏,高了就往低了猜,低了就往高了猜,范围越来越小。因此,使用二分法猜算术平方根就很自然。

一个数的平方根肯定不会超过它自己,不过直觉还告诉我们,一个数的平方根最多不会超过它的一半,例如 8 的平方根,8 的一半是 4, 4 2 = 16 > 8 4^2=16>8 42=16>8,如果这个数越大越是如此,因此我们要计算一下,这个边界是多少。为此,解如下不等式:
( a 2 ) 2 ≥ a \left(\frac{a}{2}\right)^{2} \geq a (2a)2a

意即:如果一个数的一半的平方大于它自己,那么这个数的取值范围。解以上不等式得 a ≥ 4 a≥4 a4 或者 a ≤ 0 a≤0 a0

于是边界值就是 4,那么对 0、1、2、3 分别计算结果,很容易知道,这 4 个数的平方根依次是 0、1、1、1。

注意:这 4 个特值如果没有考虑到,有可能导致你设置的搜索边界不正确。在使用二分法寻找平方根的时候,要特别注意边界值的选择

class Solution:
    def mySqrt(self, x: int) -> int:
        l = 0
        r = x
        while l <= r:
            mid = (l+r)//2
            temp = mid**2
            if  temp < x:
                l = mid + 1
            elif temp > x:
                r = mid - 1
            elif temp == x:
                return mid
        # r一定会停在mid**2 <= x的最大那个mid的位置,因为mid**2=x的mid如果存在的话在上面
        # 就已经返回了,所以这里只需要返回r就好了
        return r 

复杂度分析:

时间复杂度:O(logN),二分法的时间复杂度是对数级别的。
空间复杂度:O(1),使用了常数个数的辅助空间用于存储和比较。

2.2 牛顿法

使用牛顿法可以得到一个正实数的算术平方根,因为题目中说“结果只保留整数部分”,因此,我们把使用牛顿法得到的浮点数转换为整数即可。

牛顿法的思想:

在迭代过程中,以直线代替曲线,用一阶泰勒展式(即在当前点的切线)代替原曲线,求直线与 x 轴的交点,重复这个过程直到收敛。



注意:牛顿法得到的是平方根的浮点型精确值(可能会有一定误差),根据题目中的要求,把最后得到的这个数转换为 int 型,即去掉小数部分即可。

class Solution:

    def mySqrt(self, x):
        if x < 0:
            raise Exception('不能输入负数')
        if x == 0:
            return 0
        # 起始的时候在 1 ,这可以比较随意设置
        cur = 1
        while True:
            pre = cur
            cur = (cur + x / cur) / 2
            if abs(cur - pre) < 1e-6:
                return int(cur)

参考:

  1. LeetCode题解