问题描述
小C发现了一类特殊的整数,他称之为“疯狂整数”。疯狂整数是指只包含数字 '1' 和 '2' 的整数。举个例子,数字 1、2 和 121 都是疯狂整数。
现在,给定一个整数 N,你的任务是计算出所有小于或等于 N 的非负疯狂整数的数量。
测试样例
样例1:
输入:
N = 21
输出:5
样例2:
输入:
N = 50
输出:6
样例3:
输入:
N = 5
输出:2
解题思路
-
目标:找出所有小于或等于
N的“疯狂整数”,即只包含数字'1'和'2'的整数,并统计它们的数量。 -
方法:使用递归生成所有可能的“疯狂整数”,并在生成过程中对比每个数是否小于或等于
N。 -
步骤:
- 定义一个递归函数,用于生成疯狂整数并将符合条件的数存入列表。
- 递归从数字
'1'和'2'开始,每次递归在当前数字后面加上'1'或'2'继续生成新的数字,直到所有可能的组合都生成。 - 使用一个列表存储所有符合条件的疯狂整数,并返回其数量。
def solution(N: int) -> int:
# 用于存储所有小于或等于 N 的疯狂整数
crazy_numbers = []
# 递归函数,用于生成所有可能的疯狂整数
def generateCrazyNumbers(current):
# 将生成的字符串转换为整数进行比较
if int(current) > N:
return # 如果生成的数大于 N,就停止递归
# 将小于或等于 N 的疯狂整数加入列表
crazy_numbers.append(int(current))
# 继续生成,以 '1' 和 '2' 追加到当前字符串
generateCrazyNumbers(current + '1')
generateCrazyNumbers(current + '2')
# 从数字 '1' 和 '2' 开始递归生成疯狂整数
generateCrazyNumbers('1')
generateCrazyNumbers('2')
# 返回小于或等于 N 的疯狂整数的数量
return len(crazy_numbers)
当然也很容易想到暴力去求解,但是会有一些问题:
1. 效率问题
如果你使用遍历的方法来检查从 0 到 N 之间的每个整数,判断它是否是“疯狂整数”,这种方法的效率可能较低,尤其是当 N 很大时。例如,如果 N 是一个非常大的数字,比如几百万或更大,遍历所有数字的时间复杂度是 O(N),会导致程序运行缓慢。
2. 生成“疯狂整数”的特点
疯狂整数的定义是只包含数字 '1' 和 '2'。与其遍历每一个数并检查是否符合要求,不如直接生成所有可能的疯狂整数。因为疯狂整数的数量相对较少,我们可以通过递归的方法快速生成这些整数,而不需要遍历每个数字并进行筛选。
3. 生成方式更直接
递归方法可以更直接地生成所有符合条件的“疯狂整数”。通过递归地在 '1' 和 '2' 的基础上扩展生成,能够以较低的复杂度枚举出所有可能的组合,而不必浪费时间检查不符合条件的数。
遍历方法的劣势
假设你要用遍历方法去做:
- 遍历每个数字,从
0到N。 - 对于每个数字,将其转换为字符串,并检查字符串中的每个字符是否都是
'1'或'2'。
这样的实现思路如下:
def countCrazyNumbers(N):
count = 0
for num in range(N + 1):
if all(digit in '12' for digit in str(num)):
count += 1
return count
这个方法的问题
- 效率低:对于每个数字
num,你需要将其转换为字符串,并检查每个字符是否是'1'或'2',这会导致额外的开销。 - 不必要的检查:大部分数字(特别是较大的数字)都不符合疯狂整数的定义,因此在遍历过程中会有许多无效的检查,浪费了计算资源。
递归方法的优势
- 递归方法利用生成的特点,只生成那些符合条件的数字,不会浪费时间检查不符合条件的数。
- 递归生成的复杂度更低,特别是在
N很大的情况下,能有效避免遍历所有数字的开销。