在问 · 算法题解

69. x 的平方根

题目描述

输入非负整数 x,求它的平方根,只保留整数部分,小数部分直接丢弃。

换句话说,返回满足 r * r <= x 的最大整数 r。

不能借助语言内置的幂运算或开方,例如 pow(x, 0.5)、x ** 0.5。

示例 1
输入:x = 4
输出:2
示例 2
输入:x = 8
输出:2

解释:8 的平方根约为 2.828,去掉小数后是 2。

数据范围

  • 0 <= x <= 2^31 - 1

解题思路

答案是满足 mid*mid <= x 的最大 mid。这个条件对 mid 是单调的:小的都满足、大的都不满足,所以在 [0, x] 上二分。若 mid*mid <= x,mid 可能是答案,令 lo = mid;否则 mid 太大,令 hi = mid - 1。因为是 lo = mid,取中点必须向上取整 (lo+hi+1)//2,否则 lo+1 == hi 时会死循环。

为什么正确

答案始终在 [lo, hi] 内:lo*lo <= x,而大于 hi 的数平方都大于 x。

复杂度分析

时间 O(log x),空间 O(1)。

易错点

mid*mid 在 C++/Java 中会溢出 int,要用 long;中点向下取整配合 lo = mid 会死循环。

参考代码

Python、Java、C++、Go、JavaScript、TypeScript 六种语言,逻辑一致。

class Solution:
    def mySqrt(self, x: int) -> int:
        lo, hi = 0, x
        while lo < hi:
            mid = (lo + hi + 1) // 2
            if mid * mid <= x:
                lo = mid
            else:
                hi = mid - 1
        return lo

载入编辑器,自己跑一遍 →

其他解法

二分查找思路直接、不易出错;牛顿迭代收敛更快、迭代次数更少,但需要一点数值分析的背景。

解法 2:牛顿迭代 时间 O(log x),空间 O(1)

求 √x 等价于求 f(r)=r²-x 的正根。牛顿迭代从 r=x 开始,每一步用切线与 x 轴的交点更新,即 r ← (r + x/r) / 2。用整数除法时序列单调递减,一旦 r² ≤ x 就停止,此时的 r 恰好是 ⌊√x⌋。

收敛速度是二次的,靠近答案后每轮有效位数翻倍,实际迭代次数比二分少很多。需要理解牛顿法和整数版本的停止条件,C++/Java 中 r*r 要用 64 位整数防止溢出。

class Solution:
    def mySqrt(self, x: int) -> int:
        r = x
        while r * r > x:
            r = (r + x // r) // 2
        return r

二分查找 专题的其他题目

查看 二分查找 全部题目 →