目录
题目
题目描述
给你一个非负整数 x ,计算并返回 x 的 算术平方根 。
由于返回类型是整数,结果只保留 整数部分 ,小数部分将被 舍去 。
注意:不允许使用任何内置指数函数和算符,例如 pow(x, 0.5) 或者 x ** 0.5 。
示例1:
输入:x = 4
输出:2
示例2:
输入:x = 8
输出:2
解释:8 的算术平方根是 2.82842..., 由于返回类型是整数,小数部分将被舍去。
提示:
- 0 <= x <= 231 - 1
解题思路
- 方法一 暴力搜索
遍历[1, √x],找到最后一个i,满足i*i <= x,i即为所求。
// 方法1:暴力搜索
int mySqrt1(int x) {
if (x <= 1) return x;
long long i;
for (i = 1; i * i <= x; ++i) {
}
if (i * i == x) return i;
return i - 1;
}
- 方法二 二分法
求x平方根 <=> 求f(t) = t * t - x = 0的正根
由于f(t)是定义在[0, +∞)上的单调连续函数,因此可以找一个区间[a, b],然后不断缩小范围,来逼近f(t)=0位置。
要求f(a) * f(b) < 0,这样根t0必位于[a, b]。
可以取m = (a+b)/2,
当f(m) = 0,说明找到根了,m即为所求;
当f(m) < 0,说明a太小了,可以右移,让a=m+1;
当f(m) > 0,说明b太大了,可以左移,让b=m-1。
不断重复上面的取值,直到a > b,这样最后一个满足条件的m即为所求。
// 方法2: 二分法 在[0, x]找k, 使得k = sqrt(x) <=> k * k = x,
// ∵k是整数 ∴只保留整数t, 去掉小数部分
// ∴t*t <= k*k = x
// x平方根, 等价于求f(x) - x*x = 0的
int mySqrt2(int x) {
if (x <= 1) return x;
int left = 0, right = x, mid;
int res = x;
while (left <= right) {
long mid = left + (right - left) / 2;
if (mid * mid <= x) {
res = mid;
left = mid + 1;
}
else {
right = mid - 1;
}
}
return res;
}
- 方法三 牛顿迭代法
对于f(x)=0求根问题,
求解方法:
x2 = x1 - f(x1)/f'(x1)
不断迭代求解x1,x2,终止条件:|x2 - x1| < eps,eps是定义的精度,通常是一个比较小的浮点数
// 方法3:牛顿迭代法
// x2 = x1 - f(x1)/f'(x1), f(x) = x*x - n
// 求n平方根, 转为求f(x) = 0的正根. 这里的n就是题目中输入x
// 而f'(x) = 2x
// => x2 = x1 - (x1*x1-n)/(2x1)
// = 1/2 (x1 + n/x1)
// 含义: 通过对接近零点的领域点做切线, 不断逼近零点, 最终十分靠近零点
// 初始点, 可以任意取, 只要在合法范围内即可[0, +∞), 这里取x0=x
int mySqrt(int x) {
if (x <= 1) return x;
double x0 = x, n = x;
double x1 = x0;
static constexpr double eps = 1e-7;
while (true) {
double x2 = 0.5 * (x1 + n / x1);
if (fabs(x2 - x1) < eps) {
break;
}
x1 = x2;
}
return static_cast<int>(x1);
}