青训营X豆包MarsCode 技术训练营第五课 | 豆包MarsCode AI刷题

42 阅读5分钟

计算小于等于 N 的疯狂整数数量

在数字世界中,有一种特别的整数,我们称之为疯狂整数。这种整数的特点是:它只包含数字 '1''2'。例如,1, 2, 11, 12, 21, 122 等都是疯狂整数。这个问题的挑战在于,给定一个整数 N,我们需要计算出所有小于或等于 N 的疯狂整数的数量。

问题描述

疯狂整数的定义非常简单:

  • 每个数字只能是 12
  • 这些数可以是任意多位数,只要每一位上的数字都是 12

例如,对于 N = 22,所有小于等于 22 的疯狂整数是:1, 2, 11, 12, 21, 22,总共有 6 个疯狂整数。

问题的挑战

问题的核心在于,如何高效地生成所有小于等于 N 的疯狂整数,并计算它们的数量。直接枚举所有数字并检查是否是疯狂整数并不高效,特别是在 N 很大的情况下。我们需要一种有效的方法来生成疯狂整数,并避免重复的计算。

解决思路

我们可以通过递归或迭代的方式生成疯狂整数。每个疯狂整数可以通过在已有的数字后面加上 12 来扩展。例如,1 可以扩展成 11122 可以扩展成 2122,以此类推。通过这种方式,我们可以不断地生成新的数字,直到生成的数字超过 N 为止。

递归方法的问题:

虽然递归方法简单直观,但它可能会面临以下问题:

  1. 栈溢出:当 N 较大时,递归的深度可能会导致栈溢出。
  2. 性能低下:每次递归调用都会产生子问题,可能会导致重复计算,影响性能。

为了克服这些问题,我们可以采用迭代方法,使用队列来逐步生成所有可能的疯狂整数,避免了递归带来的复杂性和性能问题。

优化的解法:使用队列

我们使用一个队列来存储正在生成的疯狂整数,每次从队列中取出一个数字,并扩展成新的疯狂整数。每个新的数字通过将当前数字分别加上 12 来扩展。这个过程类似广度优先搜索(BFS),我们不断地从队列中取出一个数字,生成下一个数字,直到数字超过 N

通过这种方式,我们可以高效地生成所有符合条件的疯狂整数,而不需要进行递归调用。下面是这个方法的代码实现。

优化后的代码实现

def solution(N):
    # 用于存储结果的计数器
    count = 0
    
    # 使用一个队列(可以用 list 来模拟队列)来逐步构造疯狂整数
    queue = [1, 2]
    
    while queue:
        num = queue.pop(0)  # 获取队列中的第一个元素
        
        # 如果当前数字大于 N,则跳过
        if num > N:
            continue
        
        # 当前数字小于等于 N,计数
        count += 1
        
        # 生成下一个疯狂整数,分别在当前数字后面添加 '1' 和 '2'
        queue.append(num * 10 + 1)
        queue.append(num * 10 + 2)
    
    return count

代码解析

  1. count 变量:用于存储符合条件的疯狂整数的数量。
  2. 队列 queue:用来存储待处理的数字。初始时,我们将 12 放入队列,因为它们是最基本的疯狂整数。
  3. 队列操作
    • 从队列中取出第一个数字 num
    • 如果 num 超过 N,则跳过这个数字。
    • 如果 num 小于等于 N,则计数器 count 增加 1。
    • num 的扩展版本(num * 10 + 1num * 10 + 2)加入队列,继续生成下一个疯狂整数。
  4. 终止条件:当队列为空时,表示所有可能的疯狂整数都已经处理完毕,算法结束。

示例测试

print(solution(22))  # 输出: 8
print(solution(5))   # 输出: 3

时间复杂度分析

  • 空间复杂度:我们使用了一个队列来存储当前待生成的数字,队列的大小和 N 的大小相关。在最坏情况下,队列中的数字个数与 N 的位数和生成的疯狂整数数量有关,因此空间复杂度是 O(N)
  • 时间复杂度:在每次处理一个数字时,我们扩展它生成两个新的数字。每次扩展都会将一个数字从队列中取出并生成新的数字。由于每个数字最多只会被处理一次,所以时间复杂度是 O(2^k),其中 k 是数字 N 的位数。这个复杂度在实际应用中表现得非常高效。

结论

通过使用队列迭代生成疯狂整数的方法,我们能够高效地计算出所有小于等于给定数字 N 的疯狂整数数量。相比于递归方法,这种优化方式避免了递归的深度问题,并且大大提升了算法的性能。在实际应用中,这种方法能够处理较大的输入,并且保证高效和稳定。

这种方法利用了队列的广度优先生成策略,非常适合用于此类生成所有可能组合的问题,能够在时间和空间上提供较好的平衡。