☰
LogicStack-LeetCode 题解精讲:LeetCode 507. 完美数(简单)——成对因子枚举的数论模拟
2026/10/10 5:27:40 网站建设 项目流程
  • 教程
  • 文档

【免费下载链接】LogicStack-LeetCode

公众号「宫水三叶的刷题日记」刷穿 LeetCode 系列文章源码

项目地址:https://gitcode.com/gh_mirrors/lo/LogicStack-LeetCode
点击查看免费下载

本篇是「宫水三叶的刷题日记」刷穿 LeetCode 系列第 No.507 篇的深度解读。文章以 LeetCode/501-510/507. 完美数(简单).md 为核心骨架,完整继承原题解中的数学推导、成对因子枚举思路与 Java 参考代码,并结合本仓库的 Tag 归类体系,补充边界情形论证、溢出规避原理与数论背景,帮助你彻底掌握这一类「因数统计类」简单题的通用解法,读完后可直接在 LeetCode 上独立 AC 并迁移到其他因子枚举类题目。

题目描述与输入输出约定

对于一个正整数,如果它和除了它自身以外的所有正因子之和相等,我们称它为「完美数」(Perfect Number)。

给定一个整数n,如果是完美数返回true,否则返回false。

Tag:模拟、数论、数学

难度:简单

数据范围:1 <= num <= 10^8

示例

示例 1:

输入:num = 28 输出:true 解释:28 = 1 + 2 + 4 + 7 + 14 1, 2, 4, 7, 和 14 是 28 的所有正因子。

示例 2:

输入:num = 6 输出:true

示例 3:

输入:num = 496 输出:true

示例 4:

输入:num = 8128 输出:true

示例 5:

输入:num = 2 输出:false

从示例可以看到,判断的关键在于两点:一是正确枚举出除自身以外的全部正因子,二是对因子求和并与原数比较。前四个示例恰好就是最小的四个完美数 6、28、496、8128,而 2 的因子只有 1,1 ≠ 2,因此返回false。

核心思路:朴素枚举的缺陷与成对因子优化

拿到题目最容易想到的做法是从1到num - 1逐个判断能否整除并累加,但num上限是10^8,线性枚举在最坏情况下需要执行约10^8次取模运算,虽然勉强能过,但显然不是最优解。

原题解给出的关键洞察是:正因子总是成对出现的。例如28的因子对为(1, 28)、(2, 14)、(4, 7),每一对中较小的因子一定不超过sqrt(num)。因此我们只需要枚举每对正因子中的较小数,即从[1, sqrt(num)]范围内枚举即可(num > 1时成立),枚举到一个小因子i后,配对的大因子num / i可以直接算出并一并累加。

这样一来,枚举量从O(num)直接降到O(sqrt(num)),对10^8规模的数据只需约10^4次迭代。

数学解法:边界、溢出与平方根特判

原题解的参考代码如下(Java):

class Solution { public boolean checkPerfectNumber(int num) { if (num == 1) return false; int ans = 1; for (int i = 2; i <= num / i; i++) { if (num % i == 0) { ans += i; if (i * i != num) ans += num / i; } } return ans == num; } }

这段代码非常精简,背后有三个值得细讲的工程细节:

1. 为什么num == 1直接返回false

1除了自身以外没有任何正因子,因子和(空和)为0,0 ≠ 1,因此直接返回false。若不特判,下面的循环i从2开始不会执行,ans初始化为1,ans == num会错误地判定1为完美数,所以这一行是必须的。

2. 用i <= num / i代替i <= sqrt(num)的动机

原题解明确说明:使用i <= num / i作为上界判断,目的是避免调用sqrt库函数以及整数溢出。

  • 如果写i <= Math.sqrt(num),需要引入浮点库函数,浮点开方在精度与性能上都劣于纯整数比较;
  • 如果写i * i <= num,当num接近10^8时i最大约10^4,i * i不会溢出;但若题目数据范围进一步扩大,i * i在int下可能溢出为负数,从而破坏循环条件。

而i <= num / i全程使用整数除法,既无浮点误差,也不会溢出,是处理这类「以平方根为上界」问题的通用写法,在仓库其他数学类题解中同样被反复采用(参见 Index/数学.md 收录的 367. 有效的完全平方数、441. 排列硬币等题)。

3.i * i != num的平方根特判

当num是完全平方数时,其平方根i是唯一不与另一个不同因子配对的因子。例如num = 36,因子对为(1, 36)、(2, 18)、(3, 12)、(4, 9)、(6, 6),其中6只应计数一次。

因此当num % i == 0时,先累加i,再判断i * i != num:若i不是平方根,说明num / i是另一个不同的因子,一并累加;若是平方根,则num / i与i相等,跳过避免重复计数。这正是成对枚举中唯一的重复风险点,代码用一行判断干净地化解了它。

累加初值ans = 1的含义

由于循环从i = 2开始,因子1被跳过,因此将ans的初值直接设为1,等价于把1这一因子预先计入。这与题目「除了它自身以外的所有正因子之和」的定义严格对齐——1计入、num自身不计入(因为成对枚举时大因子num / i的最小取值就是i = 2时给出的num / 2,永远不会取到num本身,当num为素数时循环内一个因子都加不进去,ans恰好等于1)。

复杂度分析

  • 时间复杂度:O(sqrt(num))。循环上界为i <= num / i,迭代次数不超过sqrt(num)量级,对num = 10^8上限仅需约10^4次迭代,毫秒级完成。
  • 空间复杂度:O(1)。仅使用常数个整型变量,不依赖任何额外数据结构。

在本题数据范围下,该算法无论时间还是空间都是最优级别的表现;ans的累加值在10^8内也不会超出int表示范围。

边界用例验证与正确性论证

输入因子(除自身外)因子和结果
1无0false(代码特判)
211false
61, 2, 36true
281, 2, 4, 7, 1428true
361, 2, 3, 4, 6, 9, 12, 1855false(验证平方根不重复计数)
4961, 2, 4, 8, 16, 31, 62, 124, 248496true
8128全部真因子8128true

其中36这个用例特别适合用来检验平方根特判:若把6重复累加两次,因子和会变成61 ≠ 36,但按正确逻辑得到55 ≠ 36,依然返回false,因此特判的正确性在非完美完全平方数上体现得最清楚。

数论背景延伸:完美数家族与欧几里得-欧拉定理

本题虽标记为「简单」,背后却连接着一个经典的数论话题。完美数的研究可追溯到古希腊,最早被确认的几个完美数正是题目示例中的6, 28, 496, 8128。数论中著名的欧几里得-欧拉定理给出结论:偶完美数与梅森素数一一对应——若2^p - 1是素数,则2^(p-1) * (2^p - 1)是偶完美数;反之每个偶完美数都具有该形式。据此可以验证,在本题1 <= num <= 10^8的范围内,恰好存在 5 个完美数:6, 28, 496, 8128, 33550336,这也是另一种理论上可行的「打表」思路(仓库中另有 Index/打表.md 专题可供参考)。

值得说明的是:是否存在奇完美数至今是数论中未解决的问题,目前已知的完美数均为偶数。这些背景能帮助你理解为什么题目给出的示例恰好是这些数字,但在本题的约束下,成对因子枚举的模拟解法才是通用、稳妥且可迁移的首选。

仓库归类与延伸学习

本题在原仓库中被同时归入「数学/数论」与「模拟」两个专题:

  • Index/数学.md:收录 507. 完美数 的题解链接,与 367. 有效的完全平方数、441. 排列硬币、633. 平方数之和等因子与平方根类题目归为一类;
  • Index/模拟.md:从「按规则逐项统计」的视角将本题与 166. 分数到小数、400. 第 N 位数字 等模拟类简单题并列。

如果你希望进一步巩固本解法中用到的技巧,可以按以下顺序延伸阅读仓库内相关题解:

  1. 367. 有效的完全平方数(数学/二分):同样围绕sqrt与整数平方根判断,体会i <= num / i这类整数写法的通用性;
  2. 441. 排列硬币(数学/二分):同样需要以平方根级别上界做枚举/二分的数学题;
  3. 633. 平方数之和(数学/双指针):继续练习因子与平方根视角的组合使用;
  4. 若想挑战同类但更复杂的「因子统计」问题,可阅读 Index/数论相关题解 中难度更高的题目。

小结

LeetCode 507「完美数」是一道典型的用数学性质优化模拟的入门题。核心要点可总结为三条:

  1. 正因子成对出现,只需枚举到sqrt(num)即可覆盖所有因子,复杂度从O(num)降为O(sqrt(num));
  2. 用i <= num / i作为循环上界,避开sqrt库函数与整数乘法溢出两种隐患;
  3. 对完全平方数做i * i != num特判,避免平方根因子被重复累加,并正确处理num = 1的边界。

掌握这套「成对因子枚举」的写法后,你不仅能在本题轻松 AC,还能将它直接迁移到其他因子统计、因子和计算的题目中。完整的原始题解与仓库全部系列文章,可在 LeetCode/501-510/507. 完美数(简单).md 及 README.md 中查看。

  • 教程
  • 文档

【免费下载链接】LogicStack-LeetCode

公众号「宫水三叶的刷题日记」刷穿 LeetCode 系列文章源码

项目地址:https://gitcode.com/gh_mirrors/lo/LogicStack-LeetCode
点击查看免费下载

相关推荐

上一篇:Whispering动态分析:在运行时检测安全漏洞的方法
下一篇:从分享页到直链:LinkSwift 如何完成 8 大网盘直链解析的完整旅程

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询