有效的完全平方数

标签: 数学 二分查找

难度: Easy

给你一个正整数 num 。如果 num 是一个完全平方数,则返回 true ,否则返回 false

完全平方数 是一个可以写成某个整数的平方的整数。换句话说,它可以写成某个整数和自身的乘积。

不能使用任何内置的库函数,如  sqrt

示例 1:

输入:num = 16
输出:true
解释:返回 true ,因为 4 * 4 = 16 且 4 是一个整数。

示例 2:

输入:num = 14
输出:false
解释:返回 false ,因为 3.742 * 3.742 = 14 但 3.742 不是一个整数。

提示:

  • 1 <= num <= 231 - 1

Submission

运行时间: 17 ms

内存: 16.0 MB

class Solution(object):
  def isPerfectSquare(self, num):
      if num == 1:
          return True
      l = 1
      r = num-1
      while l <= r:
          mid = (l + r) // 2
          if num / (mid * 1.0) == mid :
              return True
          elif num / mid < mid:
              r = mid - 1
          else:
              l = mid + 1
      return False

Explain

这个题解使用二分查找的方法来判断一个数是否为完全平方数。具体思路如下: 1. 如果给定的数num等于1,直接返回True,因为1是完全平方数。 2. 初始化左边界l为1,右边界r为num-1。 3. 当l<=r时,进行以下循环: a. 计算中间值mid为(l+r)//2。 b. 如果num除以mid的结果等于mid,说明找到了完全平方数,返回True。 c. 如果num除以mid的结果小于mid,说明mid太大,将右边界r更新为mid-1。 d. 否则,num除以mid的结果大于mid,说明mid太小,将左边界l更新为mid+1。 4. 如果循环结束没有找到完全平方数,返回False。

时间复杂度: O(log n)

空间复杂度: O(1)

class Solution(object):
  def isPerfectSquare(self, num):
      if num == 1:
          return True
      
      # 初始化左右边界
      l = 1
      r = num-1
      
      while l <= r:
          # 计算中间值
          mid = (l + r) // 2
          
          # 判断 num/mid 是否等于 mid
          if num / (mid * 1.0) == mid :
              return True
          # 如果 num/mid 小于 mid,说明 mid 太大,更新右边界
          elif num / mid < mid:
              r = mid - 1
          # 如果 num/mid 大于 mid,说明 mid 太小,更新左边界
          else:
              l = mid + 1
      
      # 循环结束没有找到完全平方数,返回 False
      return False

Explore

在使用浮点数进行比较时,确实存在精度问题,尤其是当数字非常大时。在本题的上下文中,更安全的做法是避免使用浮点数比较。可以改为使用整数操作,例如检查`mid * mid == num`,这样可以精确地判断mid的平方是否等于num,无需担心浮点数的精度问题。

这种更新逻辑是安全的,不会跳过真正的完全平方数。当`num / mid < mid`时,意味着`mid * mid > num`,因此mid太大了。将右边界移动到`mid - 1`是正确的,因为任何大于或等于mid的数平方后都会大于num,不可能是我们要找的完全平方数。这个逻辑确保了不会错过正确的答案。

初始化右边界`r`为`num - 1`是因为我们已经处理了`num`等于1的情况,对于所有大于1的`num`,其完全平方根不会等于`num`本身,因此没必要将`num`作为右边界。这样做能够稍微提升效率,因为减少了搜索范围,但在大多数情况下对结果的影响不大,主要是减少了不必要的比较。

在Python中,整数类型是动态扩展的,因此理论上不会发生整型溢出问题。但在其他某些编程语言中(如Java或C++),这可能是一个问题。为了避免整型溢出,可以使用`mid = l + (r - l) / 2`来代替`mid = (l + r) / 2`。这种方法可以避免在计算mid时l和r的和超出整数的最大范围。