69.x的平方根
class Solution {
public:
int mySqrt(int a) {
// 1. 边界处理:0 的平方根还是 0
if(a==0) return a;
int l=1,r=a,mid,sqrt;
// 2. 初始化指针
// l (左指针) = 1,因为 a>0,答案至少是 1
// r (右指针) = a,因为 a 的平方根不可能大于 a 本身(a>=1 时成立)
// mid 用于存储中间值,sqrt 用于存储 a / mid 的结果
while(l<=r){ // 3. 二分查找循环 (当左指针不超过右指针时)
mid=l+(r-l)/2; // 防止溢出的写法:相当于 (l + r) / 2
sqrt=a/mid;
if(sqrt==mid){
return mid;
}else if(mid>sqrt){
// 情况2:mid > a/mid,说明 mid*mid > a,mid 太大了
// 因此答案在左半边,移动右指针
r=mid-1;
}else{
// 情况3:mid < a/mid,说明 mid*mid < a,mid 太小了
// 因此答案在右半边,移动左指针
l=mid+1;
}
}
// 4. 循环结束(l > r),返回右指针 r
// 此时 r 是最后一个满足 mid*mid <= a 的值,即向下取整的平方根
return r;
}
};
| 关键点 | 解释 |
|---|---|
| 为什么用 a / mid 而不是 mid * mid? | 假设 a = 2147483647(最大 int),如果 mid 接近 46340, mid*mid不会爆;但如果 mid 大于 46340,mid*mid瞬间溢出变成负数,导致逻辑完全错误。用除法 a/mid,结果永远在 int 范围内,绝对安全。 |
| 为什么 sqrt == mid 时直接返回? | 这代表 mid * mid == a,找到了精确的整数平方根。 |
| 为什么循环结束后返回 r 而不是 l? | 二分结束条件是 l > r。在最后一次有效移动中,l 指向了第一个平方大于 a 的数,而 r 指向了最后一个平方小于等于 a 的数。题目要求向下取整,所以返回较小的那个,即 r。 |
时间复杂度:O(loga)。每次循环将搜索区间缩小一半。
空间复杂度:O(1)。只使用了几个固定的整型变量,没有额外开辟内存。
方法二:牛顿迭代法
class Solution {
public:
int mySqrt(int a) {
long x=a;
while(x*x>a){
x=(x+a/x)/2;
}
return x;
}
};

浙公网安备 33010602011771号