题目描述

✅ 367. 有效的完全平方数

image-20260929062441172

题意分析

判断正整数 num 是否恰好等于某个整数的平方,不能调用开方函数。要求精确相等,而不是只找到平方根向下取整后的结果。

候选整数根至少为 1,至多为 num。正整数的平方随数值严格增大,因此可以直接对候选根的取值范围二分,不需要创建数组,也不需要使用浮点数近似计算。

解法:二分查找判定答案

核心思路

[!blue]

用闭区间 [left, right] 保存还可能成为整数平方根的候选,初始为 [1, num]。每轮计算中点及其平方,比较它与目标的关系。

平方等于 num 时已经找到整数根,立即返回真。平方小于目标时,中点及更小的数的平方都不够大,可以令 left = mid + 1;平方大于目标时,中点及更大的数都不可能命中,令 right = mid - 1。

每次比较都排除已经确定不合适的一半,若整数根存在,它始终留在区间内。区间只剩一个候选时也必须检查,因此循环使用 left <= right;两端交错说明所有候选都已排除,返回假。

二分初始上界是 num,中间候选可能远大于最终平方根,因此计算平方前就要使用 64 位类型。代码直接把左右边界和中点设为宽整数,保证乘法本身不会先在 32 位中溢出,再把错误结果转换成宽整数。

解题步骤

  1. 用 64 位整数初始化 left = 1、right = num。
  2. 区间非空时,计算中点 mid 和它的平方。
  3. 平方相等返回 true;偏小则移到右半边,偏大则移到左半边,两个分支都排除中点。
  4. 区间为空后仍未命中,返回 false。

代码实现

class Solution {
    public boolean isPerfectSquare(int num) {
        // 先提升为 64 位,后续平方在同一类型内计算。
        long left = 1;
        long right = num;

        while (left <= right) {
            long mid = left + (right - left) / 2;
            long square = mid * mid;

            if (square == num) {
                return true;
            }

            // 平方单调递增,本轮比较后排除中点。
            if (square < num) {
                left = mid + 1;
            } else {
                right = mid - 1;
            }
        }

        return false;
    }
}
func isPerfectSquare(num int) bool {
    // 先提升为 64 位,避免平方乘法溢出。
    target := int64(num)
    left, right := int64(1), target

    for left <= right {
        mid := left + (right-left)/2
        square := mid * mid
        if square == target {
            return true
        }
        // 平方单调递增,本轮比较后排除中点。
        if square < target {
            left = mid + 1
        } else {
            right = mid - 1
        }
    }
    return false
}

复杂度分析

  • 时间复杂度:$O(\log(num+1))$,每轮排除约一半候选。
  • 空间复杂度:$O(1)$,只保存左右边界和中间结果。

关键点总结

[!green]

  • 单调性:二分的是候选根的取值范围,不需要先有一个数组。
  • 区间配套:闭区间对应 <= 和 mid ± 1。
  • 乘法位宽:先提升操作数,再计算平方。

易错点总结

[!yellow]

  • 乘法之后才转换类型:32 位平方可能已经溢出,应先提升操作数的类型。
  • 循环使用严格小于:最后一个候选也可能是平方根,闭区间要包含两端相等的情况。
  • 更新仍保留中点:中点已确定不匹配,继续保留可能让区间不再缩小。
  • 返回最接近的整数根就算成功:目标可能位于相邻整数平方之间,必须真正命中相等条件。

相似题目

题目 难度 关联与区别
69. x 的平方根 简单 整数平方根求出后可检查平方是否恰好相等,本题只返回是否为完全平方。
319. 灯泡开关 中等 灯泡最后亮起当且仅当编号为完全平方数,因子配对解释了平方数的特殊性。
34. 在排序数组中查找元素的第一个和最后一个位置 中等 在有序或具有单调判定的区间进行边界二分;本题检查平方根边界是否精确命中,该题定位等于目标值的左右边界。
35. 搜索插入位置 简单 在有序或具有单调判定的区间进行边界二分;本题检查平方根边界是否精确命中,该题寻找第一个不小于目标的位置。
转载与许可
作者
链接 https://hgnulb.github.io/blog/2021/88914829
许可 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!