【LeetCode 刷题笔记】367.有效的完全平方数 | 二分查找经典刷题题解

一、题目描述

给你一个正整数 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 不是一个整数。

二、解题思路

  • 题目限制不能使用内置sqrt函数,需实现高效判断,最优解为二分查找 ,时间复杂度O(log n)
  • 核心需求:在[0, num]的整数范围内,寻找是否存在整数mid,使得mid² = num
  • 关键处理:mid*mid可能超出int范围导致溢出,需强转为long类型避免结果误判。

三、代码实现(Java)

解法1:

复制代码
class Solution {
    public boolean isPerfectSquare(int num) {
        // 二分查找的左右边界,初始覆盖0到num的所有整数
        int start=0;
        int end=num;
        while(start<=end){
            // 计算中间值,避免直接start+end相加导致int溢出
            int mid=start+(end-start)/2;
            // 强转为long,防止mid*mid超出int范围导致结果溢出误判
            if((long) mid*mid==num){
                // 找到满足条件的mid,直接返回true
                return true;
            }else if((long) mid*mid<num){
                // mid的平方小于num,目标值在右半区间,更新左边界
                start=mid+1;
            }else{
                // mid的平方大于num,目标值在左半区间,更新右边界
                end=mid-1;
            }
        }
        // 循环结束未找到符合条件的mid,说明不是完全平方数
        return false;
    }
}

也可以用 【LeetCode 刷题笔记】34. 在排序数组中查找元素的第一个和最后一个位置 | 二分查找经典刷题题解这篇文章的思路,本质也还是二分查找。
解法2:代码如下:

复制代码
class Solution {
    public boolean isPerfectSquare(int num) {
        // 特殊情况:num=1时直接返回true,避免后续处理边界问题
        if(num == 1){
            return true;
        }
        // 调用自定义二分实现的sqrt函数,获取num的算术平方根整数部分,转为long防止溢出
        long a = (long) sqrt(num);
        // 用a*a==num判断是否为完全平方数,必须用long类型防止溢出
        if(a * a == num){
            return true;
        }
       return false;
    }

    /**
     * 用二分查找实现整数平方根,返回x的算术平方根的整数部分
     * @param x 待求平方根的整数
     * @return x的算术平方根的整数部分
     */
    public int sqrt(int x){
        // 二分查找区间[1, x],初始左右边界
        int left = 1;
        int right = x;
        int mid = 0;
        while(left <= right){
            // 计算中间值,避免直接left+right相加导致int溢出
            mid = left + (right - left)/2;
            // 用mid <= x/mid代替mid*mid <=x,防止mid*mid超出int范围溢出
            if(mid <= x / mid){
                // mid的平方小于等于x,目标值在右半区间,更新左边界
                left = mid + 1;
            }else{
                // mid的平方大于x,目标值在左半区间,更新右边界
                right = mid - 1;
            }
        }
        // 循环结束时right即为x的算术平方根的整数部分
        return right;
    }
}

解法2注意事项:判断条件的选择:为什么只能用a * a == num

在sqrt函数中,我们返回的就是num的算术平方根的整数部分,若使用num / a == a判断,会因Java整数除法自动抹除小数部分导致误判。例如num=17时,sqrt(17)返回4num/a=17/4=4,此时num/a == a会误判为true,但44=16≠17,并非完全平方数;

而使用a * a == num时,44=16≠17,会正确返回false,只有当num是完全平方数时才会成立,无任何误判风险。


四、核心笔记 & 易错点解析

1. 整数溢出问题(高频踩坑点)

midint类型时,mid*mid的结果可能超出int的最大值(2147483647),导致结果溢出变为负数,影响判断逻辑。

解决方案:将mid强转为long后再计算乘积,即(long) mid * mid,从根源避免溢出问题。

2. 二分查找边界处理

初始边界:start=0end=num,覆盖所有可能的整数解(包括num=1num=0的特殊情况)。

循环条件:start <= end,确保区间内所有元素都被遍历,不会漏掉边界值(如num=1时,mid=1需被处理)。

区间更新逻辑:

  • mid² == num:直接返回true,无需继续查找;
  • mid² < num:目标值更大,需向右半区间查找,更新start=mid+1
  • mid² > num:目标值更小,需向左半区间查找,更新end=mid-1

3. 特殊情况验证

  • num=1时,mid=11*1=1,直接返回true
  • num=2时,循环结束未找到符合条件的mid,返回false,符合预期;
  • num=2147483647(int最大值)时,强转long的处理能避免溢出,保证判断正确。

五、复杂度分析

  • 时间复杂度O(log n),二分查找每次将区间缩小一半,最多循环log₂(num)次,符合高效判断的要求。
  • 空间复杂度O(1),仅使用常数级别的额外变量,无额外空间开销。

六、总结

本题核心是利用二分查找替代内置sqrt函数,高效判断完全平方数,关键在于处理int溢出问题和二分边界逻辑。

这类"无内置函数的平方根/平方数判断"问题,二分查找是最优解,掌握区间更新和溢出处理是解题关键。

相关推荐
江畔柳前堤34 分钟前
大语言模型分布式训练:从并行策略到万卡工程的系统梳理
人工智能·分布式·深度学习·算法·目标检测·机器学习·语言模型
Forever Nore2 小时前
LeetCode 4 寻找两个正序数组的中位数 - 二分
算法·leetcode
罗西的思考3 小时前
【OpenClaw具身硬件】MiniClaw 阅读笔记---(1)基础
人工智能·算法·机器学习
蛋先生DX4 小时前
大模型参数存储格式揭秘:BF不是男朋友
深度学习·算法·llm
爱跳舞的烤冷面5 小时前
自学嵌入式第22天(数据结构——哈希)
数据结构·算法·哈希算法
猎嘤一号5 小时前
博弈论(Game Theory)的理论、算法与工程
人工智能·算法·安全·博弈论
月光船幽幽5 小时前
影子模式下保护 logits 不被修改
人工智能·python·算法
马拉AI5 小时前
腾讯开源 Agent 记忆系统,AI“换对话就忘”的问题有了新解法(附安装使用教程)
人工智能·算法·开源·科研
地平线开发者7 小时前
征程6工具链模型X86推理方式说明
算法
地平线开发者7 小时前
【征程6】校准量化中HistogramObserver解析
算法