367. 有效的完全平方数
目录
题目链接:
题目:
解题思路:
代码:
总结:
题目链接:
367. 有效的完全平方数 - 力扣(LeetCode)
题目:
题目描述
给你一个正整数 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 \leq \text{num} \leq 2^{31} - 1\)
解题思路:
该代码利用数学规律判断完全平方数,通过连续减去奇数序列(1,3,5,...)的方式验证。若最终减至0,则为完全平方数(如16=1+3+5+7),否则不是(如14减到-2)。该方法避免了使用sqrt函数,时间复杂度为O(√n),空间复杂度O(1),代码简洁高效。其核心在于"前n个奇数之和等于n²"的数学原理,既满足题目要求,又展示了数学在算法中的巧妙应用。
代码:
class Solution {public boolean isPerfectSquare(int num) {int num1 = 1;while(num > 0) {num -= num1;num1 += 2;}return num == 0;}
}
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 不是一个整数。提示\(1 \leq \text{num} \leq 2^{31} - 1\)class Solution {
public boolean isPerfectSquare(int num) {
int num1 = 1;
while(num > 0)
{
num -= num1;
num1 += 2;
}
return num == 0;
}
}解释这个代码,字数多一点有效的完全平方数判断代码深度解析这段 Java 代码出自 Solution 类的 isPerfectSquare 方法,核心功能是判断一个正整数 num 是否为完全平方数(即能否表示为某个整数的平方)。代码采用了一种巧妙的数学规律实现判断,避免使用内置平方根函数,逻辑简洁且效率较高。以下从功能实现、数学原理、代码细节等维度展开详细解析。一、功能定位与核心需求匹配该方法的输入是一个正整数 num(范围 \(1 \leq \text{num} \leq 2^{31} - 1\)),输出是一个布尔值:
若 num 是完全平方数(如 16 = 4²),返回 true;若不是(如 14 无法表示为整数平方),返回 false。
题目明确要求 “不能使用任何内置库函数(如 sqrt)”,代码完全满足这一约束,通过纯逻辑计算实现判断,是对题目需求的精准响应。二、核心数学原理:奇数求和与完全平方数的关系代码的核心思路基于一个关键的数学规律:前 n 个连续奇数的和等于 n 的平方。即:\(1 + 3 + 5 + 7 + \dots + (2n-1) = n^2\)
例如:
\(n=1\) 时,和为 \(1 = 1^2\);\(n=2\) 时,和为 \(1+3=4 = 2^2\);\(n=3\) 时,和为 \(1+3+5=9 = 3^2\);\(n=4\) 时,和为 \(1+3+5+7=16 = 4^2\)。
反过来理解:如果一个数 num 是完全平方数(设为 \(n^2\)),那么它一定能被连续的奇数依次减去,且最终结果为 0。例如 num=16:
16 - 1 = 15
15 - 3 = 12
12 - 5 = 7
7 - 7 = 0
减到 0 时刚好减完 4 个奇数(1,3,5,7),对应 \(n=4\),因此 16 是完全平方数。
若 num 不是完全平方数(如 14):
14 - 1 = 13
13 - 3 = 10
10 - 5 = 5
5 - 7 = -2(结果小于 0,无法减完)
最终结果不为 0,因此 14 不是完全平方数。三、代码实现步骤拆解代码仅用 6 行核心逻辑实现判断,步骤清晰,可拆解为 “初始化奇数”“循环减奇数”“判断结果” 三个阶段:1. 初始化奇数:int num1 = 1;定义变量 num1 作为第一个奇数(初始值为 1),后续每次循环中会依次递增为下一个奇数(3,5,7,...)。2. 循环减奇数:while (num > 0) { num -= num1; num1 += 2; }这是代码的核心循环,通过持续用 num 减去当前奇数 num1,并更新 num1 为下一个奇数(加 2 得到下一个连续奇数),直到 num 不再大于 0 为止。
循环条件:num > 0 确保只要 num 还有剩余值,就继续减去下一个奇数;核心操作:num -= num1:用当前 num 减去当前奇数(模拟 “累加奇数和” 的逆过程);num1 += 2:将 num1 更新为下一个奇数(如 1→3→5→...),保证每次减去的都是连续奇数。3. 判断结果:return num == 0;循环结束后,若 num 恰好等于 0,说明 num 被连续奇数完全减完,即它是某个整数的平方(完全平方数),返回 true;否则返回 false。四、示例执行流程验证以两个典型示例演示代码执行过程,直观理解逻辑:示例 1:num = 16(完全平方数)初始状态:num=16,num1=1。第一次循环:num > 0 → num = 16 - 1 = 15 → num1 = 1 + 2 = 3。第二次循环:num=15 > 0 → num = 15 - 3 = 12 → num1 = 3 + 2 = 5。第三次循环:num=12 > 0 → num = 12 - 5 = 7 → num1 = 5 + 2 = 7。第四次循环:num=7 > 0 → num = 7 - 7 = 0 → num1 = 7 + 2 = 9。循环终止:num=0 不满足 num > 0 → 返回 num == 0 → true。示例 2:num = 14(非完全平方数)初始状态:num=14,num1=1。第一次循环:num=14 > 0 → num = 14 - 1 = 13 → num1=3。第二次循环:num=13 > 0 → num = 13 - 3 = 10 → num1=5。第三次循环:num=10 > 0 → num = 10 - 5 = 5 → num1=7。第四次循环:num=5 > 0 → num = 5 - 7 = -2 → num1=9。循环终止:num=-2 不满足 num > 0 → 返回 num == 0 → false。五、代码设计亮点与性能分析亮点:数学规律的巧妙应用:避开复杂的平方根计算,利用 “连续奇数和等于平方数” 的规律,将判断问题转化为简单的减法循环,逻辑直观且易于理解。无额外空间开销:仅使用两个变量(num1 和循环中的 num),空间复杂度为 \(O(1)\),属于最优级。避免浮点运算误差:完全通过整数运算实现,不存在使用 sqrt 后因浮点精度问题导致的判断错误(如 25 可能被计算为 4.999999 而非 5)。代码简洁高效:核心逻辑仅 3 行,无需复杂条件判断,可读性强,适合初学者理解。性能分析:时间复杂度:取决于 num 的大小,最坏情况下需要循环 n 次(其中 n 是 num 的平方根,如 num=10^8 时循环约 10000 次),时间复杂度为 \(O(\sqrt{num})\)。适用范围:对于题目给定的 num \leq 2^{31}-1(约 21 亿),最大循环次数约为 46340(因 \(46340^2 \approx 21\) 亿),完全在高效计算范围内,不会出现性能瓶颈。六、与其他解法的对比常见的完全平方数判断方法有 “二分查找法” 和 “内置函数法”,代码的优势如下:
对比二分查找法:
二分查找通过在 [1, num] 范围内查找是否存在整数 mid 使得 mid*mid = num,时间复杂度为 \(O(\log num)\),理论上比该代码的 \(O(\sqrt{num})\) 更快(尤其对于极大的 num)。但该代码实现更简洁,无需处理二分查找的边界条件(如 mid*mid 可能溢出的问题)。对比内置函数法:
内置函数法(如 (int)Math.sqrt(num) * (int)Math.sqrt(num) == num)虽然一行代码即可实现,但违反题目 “不能使用内置库函数” 的约束,且存在浮点精度风险。七、潜在优化点与拓展思考处理边界值:
当 num=1 时(最小输入),代码会执行一次循环(num=1-1=0),返回 true,逻辑正确。可添加 if (num == 1) return true; 作为特殊情况处理,减少一次循环判断。避免整数溢出:
题目中 num 最大为 \(2^{31}-1\),而 num1 最大可能达到 2*46340 + 1 = 92681,远小于 int 类型的最大值(21 亿),因此无需担心 num1 溢出。适用场景扩展:
该思路不仅可判断完全平方数,还可用于计算一个数的整数平方根(循环次数即平方根值),例如:java运行public int mySqrt(int num) {
int num1 = 1, count = 0;
while (num > 0) {
num -= num1;
num1 += 2;
count++;
}
return num < 0 ? count - 1 : count;
}
总结这段代码是 “利用数学规律解决算法问题” 的典型案例,通过 “连续奇数和等于平方数” 的特性,以极简的逻辑实现了完全平方数的判断。其优势在于代码简洁、无额外空间开销、避免浮点运算误差,完美符合题目的约束条件。虽然时间复杂度略高于二分查找法,但对于题目给定的输入范围,性能完全可接受。理解这段代码不仅能掌握完全平方数的判断方法,更能体会数学规律在算法设计中的核心价值 —— 将复杂问题转化为简单运算,提升代码的优雅性和可读性。
总结:
该代码利用数学规律判断完全平方数,通过连续减去奇数序列(1,3,5,...)的方式验证。若最终减至0,则为完全平方数(如16=1+3+5+7),否则不是(如14减到-2)。该方法避免了使用sqrt函数,时间复杂度为O(√n),空间复杂度O(1),代码简洁高效。其核心在于"前n个奇数之和等于n²"的数学原理,既满足题目要求,又展示了数学在算法中的巧妙应用。