统计桌面上的不同数字

标签: 数组 哈希表 数学 模拟

难度: Easy

给你一个正整数 n ,开始时,它放在桌面上。在 109 天内,每天都要执行下述步骤:

  • 对于出现在桌面上的每个数字 x ,找出符合 1 <= i <= n 且满足 x % i == 1 的所有数字 i
  • 然后,将这些数字放在桌面上。

返回在 109 天之后,出现在桌面上的 不同 整数的数目。

注意:

  • 一旦数字放在桌面上,则会一直保留直到结束。
  • % 表示取余运算。例如,14 % 3 等于 2

示例 1:

输入:n = 5
输出:4
解释:最开始,5 在桌面上。 
第二天,2 和 4 也出现在桌面上,因为 5 % 2 == 1 且 5 % 4 == 1 。 
再过一天 3 也出现在桌面上,因为 4 % 3 == 1 。 
在十亿天结束时,桌面上的不同数字有 2 、3 、4 、5 。

示例 2:

输入:n = 3 
输出:2
解释: 
因为 3 % 2 == 1 ,2 也出现在桌面上。 
在十亿天结束时,桌面上的不同数字只有两个:2 和 3 。 

提示:

  • 1 <= n <= 100

Submission

运行时间: 13 ms

内存: 16.0 MB

class Solution:
    def distinctIntegers(self, n: int) -> int:
        board={n}
        while 1:
            # try add new
            new_board=board.copy()
            for x in board:
                for i in range(1,x):
                    if x%i==1: new_board.add(i)
            if new_board!=board:  board=new_board
            elif new_board==board:break
        return len(board)

Explain

此题解使用一个集合 `board` 来存储每一天结束时桌面上的所有数字。初始时,集合中只包含数字 `n`。每天对当前集合中的每个数字 `x` 进行遍历,对于每个 `x`,再次遍历从 `1` 到 `x-1` 的所有整数 `i`,检查 `x % i == 1` 是否成立。如果条件满足,则将 `i` 添加到一个新的临时集合 `new_board` 中。如果在某一天结束时 `new_board` 和 `board` 相同,意味着没有新的数字被添加到集合中,因此可以停止迭代,返回集合中不同整数的数目。这反映出了算法的收敛性,即达到了一个固定点,新的数字不再被生成。

时间复杂度: O(n^2)

空间复杂度: O(n)

class Solution:
    def distinctIntegers(self, n: int) -> int:
        board = {n}  # 初始集合只有 n
        while True:
            new_board = board.copy()  # 复制当前集合到新集合
            for x in board:  # 遍历当前所有数字
                for i in range(1, x):  # 遍历从 1 到 x-1 的整数
                    if x % i == 1:  # 检查条件是否满足
                        new_board.add(i)  # 若满足,添加到新集合
            if new_board == board:  # 如果新旧集合相同
                break  # 停止循环
            else:
                board = new_board  # 否则更新集合
        return len(board)  # 返回集合中元素的数量

Explore

选择使用集合(set)的主要原因是集合在查找和插入操作中提供了平均时间复杂度为 O(1) 的性能,这使得每次添加和检查元素是否存在时都非常高效。对比之下,如果使用列表(list),则每次查找元素是否存在的操作的时间复杂度为 O(n),这会显著增加算法的总运行时间。此外,集合自动处理重复元素,确保存储的每个数字都是唯一的,无需额外的逻辑来处理重复性。

检查新集合`new_board`与旧集合`board`是否相同是为了确定算法是否达到了一个稳定状态,即后续不会再有新的元素被添加到集合中。这种检查是必要的,因为一旦集合中的元素不再发生变化,继续迭代将不会产生新的结果,只会增加不必要的计算。对于时间复杂度,集合比较操作通常是 O(n),其中 n 是集合中元素的数量。因此,此操作的复杂度与集合大小直接相关。

根据题解代码,算法没有明显采用特别的优化措施来减少遍历的次数或跳过某些检查。它简单地遍历从1到x-1的每个数字,检查每个数字i是否满足x % i == 1。这种方法虽然简单,但在x较大时可能效率不高。一种可能的优化方法是在确定某些数学属性(例如因子的特征)后减少检查范围,但在当前算法实现中并未体现这种优化。