题目难度: 简单

原题链接

今天继续更新 Leetcode 的剑指 Offer(专项突击版)系列, 大家在公众号 算法精选 里回复 剑指offer2 就能看到该系列当前连载的所有文章了, 记得关注哦~

题目描述

给定一个非负整数 x ,计算并返回 x 的平方根,即实现 int sqrt(int x) 函数。

正数的平方根有两个,只输出其中的正数平方根。

如果平方根不是整数,输出只保留整数的部分,小数部分将被舍去。

示例 1:

  • 输入: x = 4
  • 输出: 2

示例 2:

  • 输入: x = 8
  • 输出: 2
  • 解释: 8 的平方根是 2.82842...,由于小数部分将被舍去,所以返回 2

提示:

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

题目思考

  1. 如何优化时间复杂度?

解决方案

思路

  • 分析题目, 最容易想到的思路就是从 0 开始遍历, 判断当前数字的平方是否大于 x, 是的话就返回当前数字减 1
  • 不过这样时间复杂度达到了 O(sqrt(x)), 如何优化呢?
  • 对于区间[0,x]而言, 其每个数字的平方都满足单调递增, 我们可以利用这一点, 采用二分查找的方式加速
  • 也就是二分查找平方值小于等于 x 的最大整数, 因为实际平方根可能是小数, 而题目要求只保留整数部分, 所以对应整数的平方不一定恰好等于 x, 也可能小于 x
  • 具体做法如下:
    1. 初始化左右边界 s 和 e 分别为 0 和 x, 代表查找整个区间
    2. 初始化最终待求的平方根 res 为 0
    3. 使用 while 循环保证当前查找范围有效, 即满足 s<=e
    4. 计算当前两者中点 m, 并比较其平方和 x 的关系
    5. 如果 m 的平方小于等于 x, 则说明 m 可能是平方根, 将 res 更新为两者的较大值, 然后继续查找右半部分
    6. 如果 m 的平方大于 x, 则 m 肯定不可能是平方根, 继续查找左半部分
    7. 最后, 遍历完整个区间后的 res 即为 x 的平方根
  • 下面代码中有详细的注释, 方便大家理解

复杂度

  • 时间复杂度 O(logx): 需要在区间[0,x]之间进行二分查找, 时间复杂度是 O(logx)
  • 空间复杂度 O(1): 只使用了几个常数空间的变量

代码

class Solution:
    def mySqrt(self, x: int) -> int:
        # 二分查找
        s, e = 0, x
        res = 0
        while s <= e:
            m = (s + e) >> 1
            if m * m <= x:
                # 当前m的平方不超过x, 取最大可能的m
                res = max(res, m)
                # 然后向右查找
                s = m + 1
            else:
                # 当前m的平方超过了x, 一定不是平方根, 向左查找
                e = m - 1
        return res

大家可以在下面这些地方找到我~😊

我的 GitHub

我的 Leetcode

我的 CSDN

我的知乎专栏

我的头条号

我的牛客网博客

我的公众号: 算法精选, 欢迎大家扫码关注~😊

算法精选 - 微信扫一扫关注我