
笔记记录了经典的**"二分查找求平方根"** 问题。这是二分查找算法在非有序数组场景下的一个重要变种 ,核心是利用数学函数的单调性来应用二分法。
以下是对该笔记内容的详细解析和标准代码实现:
1. 算法原理
对于一个非负整数 x,其平方根函数 f(k)=k2在 k≥0的区间内是单调递增的。
-
单调性:如果 k1<k2,那么 k12<k22。
-
二段性 :我们可以在区间 [1,x]上查找,对于任意中间值
mid:-
如果
mid * mid <= x:说明平方根在[mid, x]之间(或者mid就是答案,需尝试更大的值看是否有更接近的)。 -
如果
mid * mid > x:说明平方根一定在[1, mid - 1]之间。
-
2. 笔记关键点解析
-
边界条件 :
if(x < 1) return 0;。这是为了处理 x=0的情况,防止后续right=x导致死循环或越界。 -
搜索区间 :
left = 1,right = x。对于 x≥1,其平方根不会超过 x本身。 -
防溢出技巧:
-
笔记中使用了
long mid = ...。 -
原因 :在计算
mid * mid时,如果x接近整型最大值(约 21 亿),mid约为 10 万,mid * mid将达到 100 亿,远超int的范围(约 21 亿),导致整数溢出 ,计算结果变为负数从而出错。使用long类型可以完美规避这个问题。
-
3. 代码实现(Java)
public class SqrtX {
public int mySqrt(int x) {
// 特殊情况处理:0的平方根是0
if (x < 1) return 0;
// 定义搜索区间 [left, right]
int left = 1;
int right = x;
while (left <= right) {
// 计算 mid,使用 long 防止乘法溢出,使用 left + (right - left) / 2 防止加法溢出
long mid = left + (right - left) / 2;
long square = mid * mid;
if (square == x) {
// 正好找到完全平方数
return (int) mid;
} else if (square < x) {
// 平方小于 x,说明平方根在右边,尝试更大的数
left = (int) mid + 1;
} else {
// 平方大于 x,说明平方根在左边
right = (int) mid - 1;
}
}
// 循环结束时,left > right。
// 此时 right 指向的是平方小于等于 x 的最大整数(即向下取整的结果)。
// 举例:x=8, 循环结束时 right=2, left=3。返回 right。
return right;
}
}
4. 进阶:牛顿迭代法(拓展)
虽然二分查找是该题的标准解法,但计算机科学中求解平方根更高效的算法是牛顿迭代法(Newton's Method)。它的收敛速度比二分查找更快(二次收敛)。
核心思想:通过不断作切线来逼近方程 f(k)=k2−x=0的根。
迭代公式:kn+1=21(kn+knx)
public int mySqrtNewton(int x) {
if (x == 0) return 0;
double C = x;
double x0 = x;
while (true) {
// 牛顿迭代公式
double xi = 0.5 * (x0 + C / x0);
// 如果变化量极小,认为已经收敛到精确解
if (Math.abs(xi - x0) < 1e-7) break;
x0 = xi;
}
return (int) x0;
}