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).
♥ 如有错误或有其他解题思路,欢迎指出~~~φ(≧ω≦*)♪