题目
给你一个非负整数
x
,计算并返回x
的 算术平方根 。由于返回类型是整数,结果只保留 整数部分 ,小数部分将被 舍去 。
注意:不允许使用任何内置指数函数和算符,例如
pow(x, 0.5)
或者x ** 0.5
。
简单地说,就是写个开根号的算法。
我的题解
牛顿迭代法
通过上述链接中马同学高赞回答,可以清晰地明白牛顿迭代法的几何意义:以直代曲。最后求解出的迭代通式如下:
由于这里是开方计算,即相当于求解方程: 的解x 。那么带入通式并简单化简后,得:
时间复杂度:O(logx)。是二次收敛的,相较于二分查找更快。
空间复杂度:O(1)。
class Solution:
def mySqrt(self, x: int) -> int:
xn=1 # 起始值可以随便定一个大于0的数
gap=1
while gap !=0:
xm=(xn+x/xn)/2
gap=xm//1-xn//1
xn=xm
return int(xn//1)
Accepted
- 1017/1017 cases passed (36 ms)
- Your runtime beats 77.21 % of python3 submissions
- Your memory usage beats 65.15 % of python3 submissions (14.9 MB)
官方题解
法1:袖珍计算器算法
用指数函数exp和对数函数ln 代替平方根函数。有如下等价变换:
时间复杂度:O(1);由于内置的exp和ln函数计算都很快,我们这里认为时间复杂度为O(1)。
空间复杂度:O(1)。
由于浮点数计算由于位数大小的原因会进行近似,结果是不精确的。尤其是取整的时候,需要判断ans和ans+1哪一个是正确的。即,如果ans+1的平方都比x小,那么ans+1肯定是最大的整数解,否则就是ans。
import math
class Solution:
def mySqrt(self, x: int) -> int:
if x==0:
return 0
ans=int((math.exp(0.5*math.log(x))))
return ans+1 if (ans+1)**2 <=x else ans
Accepted
- 1017/1017 cases passed (24 ms)
- Your runtime beats 99.53 % of python3 submissions
- Your memory usage beats 11.19 % of python3 submissions (15 MB)
法2:二分查找
因为ans=k是的最大整数解,所以可以进行二分查找。
设定下界为0,上界则粗略设为x。每次对mid的平方与x值比较大小,再变换上下界,缩小范围。由于都是整数变换,也不用和法1一样比较ans与ans+1谁更合适了。
时间复杂度:O(logx)
空间复杂度:O(1)
class Solution:
def mySqrt(self, x: int) -> int:
l,r,ans=0,x,-1
while l<=r:
mid=(l+r)//2
if mid*mid <=x:
ans=mid
l=mid+1
else:
r=mid-1
return ans
Accepted
- 1017/1017 cases passed (28 ms)
- Your runtime beats 97.5 % of python3 submissions
- Your memory usage beats 65.49 % of python3 submissions (14.9 MB)
总结
1. 通过其他内置函数进行运算,要快于其他算法,因为只有计算,而没有多次的遍历或者迭代。
2. 理论上,牛顿迭代法更快。如果起始点有更好的确定方法,应该是能大大优化速度。