以下是计算非负整数 x 的算术平方根的 C 语言实现代码,包含逐行解析:
#include
// 计算并返回 x 的算术平方根(整数部分)
int mySqrt(int x) {
// 处理 x 为 0 或 1 的特殊情况
if (x < 2) {
return x; // 直接返回自身
}
// 初始化二分查找的左右边界
int left = 1; // 最小可能的平方根
int right = x; // 最大可能的平方根(实际会快速缩小范围)
int ans = 0; // 保存最终结果
// 二分查找核心逻辑
while (left <= right) {
// 计算中间值(避免整数溢出)
int mid = left + (right - left) / 2;
// 将 mid 的平方转为 long long 类型防止溢出
long long square = (long long)mid * mid;
if (square == x) {
// 找到精确平方根,直接返回
return mid;
} else if (square < x) {
// 当前 mid 可能是候选,继续向右查找更大的值
ans = mid; // 更新候选值
left = mid + 1; // 缩小左边界
} else {
// mid 的平方太大,缩小右边界
right = mid - 1;
}
}
// 返回最大的满足条件的整数
return ans;
}
/**************************** 测试代码 ****************************/
int main() {
// 测试用例1:完全平方数
printf("sqrt(4) = %d\n", mySqrt(4)); // 输出: 2
// 测试用例2:非完全平方数
printf("sqrt(8) = %d\n", mySqrt(8)); // 输出: 2
// 测试用例3:边界值1
printf("sqrt(1) = %d\n", mySqrt(1)); // 输出: 1
// 测试用例4:边界值0
printf("sqrt(0) = %d\n", mySqrt(0)); // 输出: 0
// 测试用例5:大数值(46339^2 = 2147482921 < 2147395599)
printf("sqrt(2147395599) = %d\n", mySqrt(2147395599)); // 输出: 46339
return 0;
}
逐行解析
1. 函数签名
int mySqrt(int x)
- 功能:接收一个非负整数 x,返回其算术平方根的整数部分。
2. 处理特殊情况
if (x < 2) {
return x;
}
- 逻辑:当 x 为 0 或 1 时,平方根就是自身。
- 必要性:避免后续二分查找的冗余计算。
3. 初始化二分查找边界
int left = 1; // 最小可能的平方根
int right = x; // 最大可能的平方根(实际会快速缩小范围)
int ans = 0; // 保存最终结果
- left:从 1 开始查找(因为 x >= 2 时平方根至少为 1)。
- right:初始设为 x(虽然实际平方根不可能超过 x/2,但代码仍能正确收敛)。
- ans:记录当前满足条件的最大候选值。
4. 二分查找核心逻辑
while (left <= right) {
// 计算中间值(避免整数溢出)
int mid = left + (right - left) / 2;
// 将 mid 的平方转为 long long 类型防止溢出
long long square = (long long)mid * mid;
if (square == x) {
return mid; // 找到精确平方根
} else if (square < x) {
ans = mid; // 更新候选值
left = mid + 1; // 向右查找
} else {
right = mid - 1; // 向左查找
}
}
- 防溢出技巧:
- mid = left + (right - left) / 2:避免 (left + right) 的潜在溢出。
- long long square:确保大数平方计算不会溢出。
- 三种情况处理:
- 精确命中:直接返回 mid。
- 平方太小:记录候选值 ans,向右缩小范围。
- 平方太大:向左缩小范围。
5. 返回结果
return ans;
- 最终结果:循环结束时,ans 保存的是满足 mid^2 <= x 的最大整数。
测试代码解析
// 测试用例1:完全平方数
printf("sqrt(4) = %d\n", mySqrt(4)); // 输出: 2
// 测试用例2:非完全平方数
printf("sqrt(8) = %d\n", mySqrt(8)); // 输出: 2
// 测试用例3:边界值1
printf("sqrt(1) = %d\n", mySqrt(1)); // 输出: 1
// 测试用例4:边界值0
printf("sqrt(0) = %d\n", mySqrt(0)); // 输出: 0
// 测试用例5:大数值验证
printf("sqrt(2147395599) = %d\n", mySqrt(2147395599)); // 输出: 46339
- 覆盖场景:
- 完全平方数、非完全平方数。
- 边界值 0 和 1。
- 接近 INT_MAX 的大数值。
关键特性总结
特性 | 说明 |
时间复杂度 | O(log n) → 二分查找的高效性 |
空间复杂度 | O(1) → 仅使用固定数量的变量 |
防溢出设计 | 使用 long long 存储平方值,避免中间计算溢出 |
边界条件处理 | 显式处理 x < 2 的情况 |
适用范围 | 所有非负整数(包括 INT_MAX) |
扩展讨论
如果需要实现浮点数精度的平方根(例如保留 3 位小数),可以使用 牛顿迭代法(Newton-Raphson Method)。其核心思想是通过迭代逼近真实值,代码示例如下:
double sqrtNewton(double x) {
if (x < 0 return -1 double guess='x;' double epsilon='1e-7;' while fabsguess guess - x> epsilon) {
guess = (guess + x / guess) / 2.0;
}
return guess;
}
本文暂时没有评论,来添加一个吧(●'◡'●)