LeetCode 69 Sqrt(x)

题目-x 的平方根

【英文版】https://leetcode.com/problems/sqrtx/
【中文版】https://leetcode-cn.com/problems/sqrtx/

实现 int sqrt(int x) 函数。
计算并返回 x 的平方根,其中 x 是非负整数。
由于返回类型是整数,结果只保留整数的部分,小数部分将被舍去。

示例

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

库函数

直接使用math库的开根号函数。似乎是用C语言实现的二分查找法(含底层优化计算),和LeetCode的这篇解答很像:2^幂次底层优化运算。源码在这里找:python/cpython

Library Function-python

import math

class Solution(object):
    def mySqrt(self, x):
        return int(math.sqrt(x))

Runtime: 20 ms, faster than 77.82% of Python online submissions for Sqrt(x).
Memory Usage: 11.7 MB, less than 54.90% of Python online submissions for Sqrt(x).

暴力求解

遍历x//2+1,如果第i个数刚好使得i*i<=x and (i+1)*(i+1)>x则返回i
但是这种方法会用Runtime Error。
★ 时间复杂度:\(O(x//2+1)\)
★ 空间复杂度:\(O(1)\)

Brute Force-python

class Solution(object):
    def mySqrt(self, x):
        for i in range(1,x//2+1):
            if i*i<=x and (i+1)*(i+1)>x: return i

Runtime Error

二分查找

一个数的平方根最小是0(x == 0),最大是x//2+1(+1是为了避免x == 1,整除2为0的情况)。
二分查找的注意细节可看这篇博文:LeetCode 35 Search Insert Position
★ 时间复杂度:\(O(logn)\)
★ 空间复杂度:\(O(1)\)

Binary Search-python

class Solution(object):
    def mySqrt(self, x):
        left = 0
        right = x // 2 + 1
        while left < right:
            mid = left + (right - left + 1) // 2
            if mid * mid > x:
                right = mid - 1
            elif (mid + 1) * (mid + 1) > x:
                return mid
            else:
                left = mid
        return left

Runtime: 20 ms, faster than 77.82% of Python online submissions for Sqrt(x).
Memory Usage: 11.6 MB, less than 96.08% of Python online submissions for Sqrt(x).

 

牛顿法

该部分由整理liweiwei1419的LeetCode 69解答而来,推荐有空的小伙伴可以看下原作更详细的回答。

思想
在迭代过程中,以直线代替曲线,用一阶泰勒展式(即在当前点的切线)代替原曲线,求直线与x轴的交点,重复这个过程直到收敛。
[1]
关于牛顿法更详细的讲解可看这篇知乎回答:如何通俗易懂地讲解牛顿迭代法求开方

• 用迭代法近似地解一个方程的注意事项:
  ① 迭代是否收敛
  ② 收敛时是否收敛于我们想要的解
题解
我们仅仅是不断用(x, f(x))的切线来逼近方程\(f(x)=x^2-a\)的根。\(\sqrt{a}\)实际上就是方程\(x^2-a=0\)的一个正实根,这个函数的导数是\({f}'(x)=2x\)。也就是说,函数上任一点(x, f(x))处的切线斜率是2x。那么,\(x-\frac{f(x)}{2x}\)就是一个比x更接近的近似值。代入\(f(x)=x^2-a\)得到\(x-\frac{x^2-a}{2x}\),化简得\(\frac{x^2+a}{2x}\)[2]
[3]
不过牛顿法固然从数学上减少了迭代,但没有考虑计算量。牛顿法需要计算切线和坐标轴的交点,这个地方的运算量已经抵得上一次普通二分了。
★ 时间复杂度:\(O(logn)\)
★ 空间复杂度:\(O(1)\)

Python的float除法和整除法

不同版本的python对整数/整数的处理不同:
教老版本的python:整数/整数---整除法
较新版本的python:整数/整数---float除法

最保险的做法应明确告诉编译器你想要的结果,即:
整除法:用//,e.g.数//数
float除法:用/且除数与被除数中有一个数是浮点数,e.g.float/数数/float

其他Python常见陷阱,可看我的这篇博客:Python陷阱

Newton_Loop-python

class Solution(object):
    def mySqrt(self, x):
        if x == 0: return 0
        a = 1
        while True:
            pre = a
            a = 1.0 / 2 * (a + x / a)
            if abs(a - pre) < 1:
                return int(a)

Runtime: 20 ms, faster than 77.82% of Python online submissions for Sqrt(x).
Memory Usage: 11.7 MB, less than 58.82% of Python online submissions for Sqrt(x).

• 若写a = 1 / 2 * (a + x / a)在IDE上能正常运行,但是提交到LeetCode会报除0错误,可能由于LeetCode上用的Python版本较老。
改为a = (a + x / a) / 2。
这种方法会报超时错误Time Limit Exceeded
最佳改法:a = 1.0 / 2 * (a + x / a)才能在leetcode正常提交。

Newton_Recursion-python

class Solution(object):
    def sqrts(self, x, a):
        res = (a + x / a) / 2.0
        if int(res) == int(a):
            return int(res)
        else:
            return self.sqrts(x, res)

    def mySqrt(self, x):
        if x == 0: return 0
        return self.sqrts(x, 1)

Runtime: 20 ms, faster than 77.82% of Python online submissions for Sqrt(x).
Memory Usage: 11.8 MB, less than 54.90% of Python online submissions for Sqrt(x).

 

如有错误或有其他解题思路,欢迎指出~~~φ(≧ω≦*)♪

同类型题目

  1. LeetCode 35 Search Insert Position

  2. LeetCode 704 Binary Search

参考


  1. 牛顿法gif图 ↩︎

  2. LeetCode牛顿法题解 ↩︎

  3. 牛顿法图 ↩︎

posted @ 2019-08-25 16:37  维夏十四  阅读(203)  评论(0编辑  收藏  举报