字节跳动青训营刷题笔记4| 豆包MarsCode AI刷题

56 阅读4分钟

问题描述

小C发现了一类特殊的整数,他称之为“疯狂整数”。疯狂整数是指只包含数字 '1' 和 '2' 的整数。举个例子,数字 12 和 121 都是疯狂整数。

现在,给定一个整数 N,你的任务是计算出所有小于或等于 N 的非负疯狂整数的数量。


测试样例

样例1:

输入:N = 21
输出:5

样例2:

输入:N = 50
输出:6

样例3:

输入:N = 5
输出:2

解题思路

  1. 目标:找出所有小于或等于 N 的“疯狂整数”,即只包含数字 '1''2' 的整数,并统计它们的数量。

  2. 方法:使用递归生成所有可能的“疯狂整数”,并在生成过程中对比每个数是否小于或等于 N

  3. 步骤

    • 定义一个递归函数,用于生成疯狂整数并将符合条件的数存入列表。
    • 递归从数字 '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. 效率问题

如果你使用遍历的方法来检查从 0N 之间的每个整数,判断它是否是“疯狂整数”,这种方法的效率可能较低,尤其是当 N 很大时。例如,如果 N 是一个非常大的数字,比如几百万或更大,遍历所有数字的时间复杂度是 O(N),会导致程序运行缓慢。

2. 生成“疯狂整数”的特点

疯狂整数的定义是只包含数字 '1''2'。与其遍历每一个数并检查是否符合要求,不如直接生成所有可能的疯狂整数。因为疯狂整数的数量相对较少,我们可以通过递归的方法快速生成这些整数,而不需要遍历每个数字并进行筛选。

3. 生成方式更直接

递归方法可以更直接地生成所有符合条件的“疯狂整数”。通过递归地在 '1''2' 的基础上扩展生成,能够以较低的复杂度枚举出所有可能的组合,而不必浪费时间检查不符合条件的数。


遍历方法的劣势

假设你要用遍历方法去做:

  1. 遍历每个数字,从 0N
  2. 对于每个数字,将其转换为字符串,并检查字符串中的每个字符是否都是 '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 很大的情况下,能有效避免遍历所有数字的开销。