leetcode
leetcode 451 ~ 500
完美数

完美数

难度:

标签:

题目描述

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

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

 

示例 1:

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

示例 2:

输入:num = 7
输出:false

 

提示:

  • 1 <= num <= 108

代码结果

运行时间: 24 ms, 内存: 16.6 MB


/*
 * 思路:
 * 使用 Java 8 的流(Stream)API 来简化因子的求和。
 * 我们可以利用 IntStream 来生成从 1 到 num/2 的范围,并筛选出所有的因子,最后求和。
 */
import java.util.stream.IntStream;
public class PerfectNumberStream {
    public boolean checkPerfectNumber(int num) {
        if (num <= 1) return false; // 1 不是完美数
        int sum = IntStream.rangeClosed(1, num / 2)
                           .filter(i -> num % i == 0)
                           .sum();
        return sum == num;
    }
}

解释

方法:

此题解的核心思想是利用数学方法减少不必要的迭代。对于一个给定的正整数num,如果它是一个完美数,那么它所有的正因子之和(除了它自身)应该等于num。题解中首先排除了1,因为1没有除了自身以外的正因子。接着,从2开始遍历到sqrt(num),寻找所有可能的因子。如果d是num的因子,则num/d也是num的因子。这样可以在O(sqrt(n))的时间复杂度内找到所有的因子。对于每个找到的因子d,如果d*d不等于num,那么可以将num/d也加入到因子之和中。最后,比较计算出的因子之和与num是否相等,从而判断是否为完美数。

时间复杂度:

O(sqrt(n))

空间复杂度:

O(1)

代码细节讲解

🦆
为什么题解中提到'从2开始遍历到sqrt(num)',而不是直接遍历到num?这样做的数学原理是什么?
遍历到sqrt(num)的原因是因为对任何一个数num而言,如果它可以被d整除(d <= sqrt(num)),那么必定存在一个配对的因子num/d,且这个配对因子必然大于或等于sqrt(num)。这意味着所有大于sqrt(num)的因子都可以在小于等于sqrt(num)时找到其对应的小因子。因此,只需遍历到sqrt(num),就可以通过d和num/d获得所有因子,不需要遍历到num,这大大减少了计算量。
🦆
在算法中,如果`d * d == num`,为什么不需要将num/d加入因子之和?这样做的原因是什么?
当`d * d == num`时,意味着d是num的一个平方根,因此d和num/d实际上是同一个数。在这种情况下,如果将num/d加入因子之和,实际上就是重复计算了d。为了避免因子之和被重复计算,只需加入d即可。例如,对于num = 36,当d = 6时,d * d = 36,此时无需再加入36/6=6,因为这会重复计算因子6。
🦆
题解中提到的时间复杂度是O(sqrt(n)),这个复杂度是如何计算得出的?
时间复杂度O(sqrt(n))来自于算法中遍历因子的过程。因为算法只检查从2到sqrt(num)范围内的所有整数是否为num的因子,因此遍历的次数大约是sqrt(num)次。由于每次检查操作(包括求余、加法和条件判断)可以视为常数时间操作,所以总的时间复杂度是O(sqrt(n))。这表示算法的执行时间与num的平方根成正比,大大提高了效率,尤其是对于非常大的数。

相关问题

自除数

自除数 是指可以被它包含的每一位数整除的数。

  • 例如,128 是一个 自除数 ,因为 128 % 1 == 0128 % 2 == 0128 % 8 == 0

自除数 不允许包含 0 。

给定两个整数 left 和 right ,返回一个列表,列表的元素是范围 [left, right] 内所有的 自除数

 

示例 1:

输入:left = 1, right = 22
输出:[1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 15, 22]

示例 2:

输入:left = 47, right = 85
输出:[48,55,66,77]

 

提示:

  • 1 <= left <= right <= 104