Solution 1
这道题乍一看应该是二分查找,不断尝试枚举整数的平方结果和输入的关系来确定平方根的整数位。但这样做有两个问题:需要留意乘法结果可能会超出int甚至是long;控制精度也不灵活。然后我上网上搜索了一下,发现了之前学习的牛顿搜索法可以解决这个问题。
这里用到的其实就是用牛顿法解方程,通过更快的下降方法实现虽然理论时间复杂度相同,但是实际速度比二分查找快的解决方案。记输入为 C C C,其对应平方根为 x x x,那么确定一个函数关系:
f ( x ) = x 2 − C f(x) = x^2 - C f(x)=x2−C
平方根就是这个函数取值为0等时候。那么构造牛顿法迭代式:
x i + 1 = x i − f ( x i ) f ′ ( x i ) = x i 2 + C 2 x i x_{i + 1} = x_i - \frac{f(x_i)}{f^\prime(x_i)} = \frac{x_i}{2} + \frac{C}{2 x_i} xi+1=xi−f′(xi)f(xi)=2xi+2xiC
整体来看,牛顿法的搜索步长是二次步进,因此时间复杂度是对数线性复杂度,但是由于方程本身是凸函数,搜索速度实际上比二分快很多,而且搜索质量可以通过误差上界控制。
- 时间复杂度: O ( log ( n ) ) O(\log(n)) O(log(n)),上面已经解释了,实际实现比二分快
- 空间复杂度: O ( 1 ) O(1) O(1),因为只维护常数个状态量
class Solution {
public:
int mySqrt(int x) {
if (x == 0) {
return 0; // 不然后面有一步计算迭代会出问题
}
// 牛顿下降
double C = x, xPrev = x, eps = 1e-7;
while (true) {
double xNext = 0.5 * (xPrev + C / xPrev);
if (fabs(xPrev - xNext) < eps) {
break;
}
xPrev = xNext;
}
return int(xPrev);
}
};
Solution 2
Solution 1的Python实现
class Solution:
def mySqrt(self, x: int) -> int:
if x == 0: return 0
C = float(x)
xPrev = float(x)
eps = 1e-7
while True:
xNext = 0.5 * (xPrev + C / xPrev)
if math.fabs(xPrev - xNext) < eps: break
xPrev = xNext
return int(xPrev)