一、题目描述
给你一个非负整数 x
,计算并返回 x
的 算术平方根 。
由于返回类型是整数,结果只保留 整数部分 ,小数部分将被 舍去 。
注意:不允许使用任何内置指数函数和算符,例如 pow(x, 0.5)
或者 x ** 0.5
。
示例 1:
输入:x = 4 输出:2
示例 2:
输入:x = 8 输出:2 解释:8 的算术平方根是 2.82842..., 由于返回类型是整数,小数部分将被舍去。
提示:
0 <= x <= 2^31 - 1
二、解题思路
1. 问题转化:将求整数 x
的平方根转化为在整数范围内寻找一个数 y
,使得 y * y
最接近但不超过 x
。
2. 二分查找:初始化两个指针 left
和 right
,分别指向 0
和 x
。
3. 迭代过程:在 left
和 right
之间进行二分查找。
4. 中间值计算:在每次迭代中,计算中间值 mid
,其中 mid = left + (right - left) / 2
。
5. 条件判断:
- 如果
mid * mid
等于x
,则返回mid
。 - 如果
mid * mid
小于x
,则将left
设置为mid + 1
。 - 如果
mid * mid
大于x
,则将right
设置为mid - 1
。
6. 结束条件:当 left
大于 right
时,right
就是我们要找的答案。
7. 特殊情况处理:当 x
为 0 或 1 时,直接返回 x
。
8. 结果返回:返回通过二分查找找到的平方根的整数部分。
三、具体代码
class Solution {
public int mySqrt(int x) {
if (x == 0 || x == 1) {
return x;
}
int left = 1;
int right = x;
int result = 0;
while (left <= right) {
int mid = left + (right - left) / 2;
if (mid <= x / mid) {
left = mid + 1;
result = mid;
} else {
right = mid - 1;
}
}
return result;
}
}
四、时间复杂度和空间复杂度
1. 时间复杂度
- 外层循环的次数取决于
x
的值。在最坏的情况下,当x
接近 2^31 - 1 时,外层循环可能需要进行大约 16 次迭代(因为 2^31 - 1 的平方根大约是 46000)。 - 每次迭代中,我们执行一次加法、一次减法、一次除法(或取模运算)和一次乘法。这些操作的时间复杂度都是 O(1)。
- 因此,总的时间复杂度是 O(log(x)),其中
x
是输入的整数。
2. 空间复杂度
- 代码中只使用了几个变量,如
left
、right
和result
,这些变量的大小与输入无关,因此它们的空间复杂度是 O(1)。 - 没有使用额外的数据结构,如数组或链表,因此空间复杂度不会超过 O(1)。
五、总结知识点
-
整数平方根的计算:代码实现了一个函数
mySqrt
,用于计算一个非负整数x
的平方根。 -
二分查找算法:使用二分查找算法来逼近
x
的平方根。二分查找是一种在有序数组中查找特定元素的搜索算法,其时间复杂度为 O(log n),其中 n 是数组的长度。 -
边界条件处理:在函数开始时,对特殊情况
x == 0
和x == 1
进行了特殊处理,直接返回x
,因为这些情况下平方根就是x
。 -
位运算:在计算中间值
mid
时,使用了位运算(right - left) / 2
来避免整数溢出的问题。 -
条件判断:使用
if-else
语句来根据mid * mid
与x
的关系调整搜索范围。 -
循环结构:使用
while
循环来迭代搜索过程,直到找到合适的平方根。 -
整数运算:代码中使用了整数运算,包括加法、减法、除法和取模运算。
-
返回结果:函数返回找到的平方根的整数部分。
-
错误处理:虽然在这个特定的问题中没有直接处理错误情况,但代码的结构允许在将来扩展错误处理逻辑。
以上就是解决这个问题的详细步骤,希望能够为各位提供启发和帮助。