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

34 阅读4分钟

第498题 量化数字的统计

题目分析

题目要求生成长度为 n 的所有数字,这些数字满足每两个连续数字之间的差的绝对值为 k,并且这些数字是非负整数。除了数字 0 外,任何生成的数字都不能有前导零。例如,01 是无效的。

此外,我们需要从小到大返回所有符合条件的整数。

解题思路

  • 基础分析:

    • 对于长度为 n = 1 的情况,所有的数字 [0, 1, 2, ..., 9] 都是合法的,因为它们只有一位,不需要考虑连续数字之间的差的条件。
    • 对于 n > 1 的情况,每个数字的第二位及以后每一位都需要满足与前一位数字的差的绝对值为 k,即 |a_i - a_(i-1)| = k,其中 a_i 是第 i 位的数字。
    • 需要生成所有合法的数字,并确保这些数字没有前导零(例如,01012 是无效的)。
  • 递归和回溯:

    • 使用回溯法(Backtracking)来递归生成所有符合条件的数字。从第一位开始,尝试每个可能的数字(对于长度为 n 的数字,第一位不能为零),并递归地生成后续数字。
    • 对于每一位,检查当前数字与前一位的差是否满足 |a_i - a_(i-1)| = k 的条件。如果满足条件,就继续递归生成下一位,否则回溯。
  • 边界情况:

    • n = 1 时,所有数字 [0, 1, 2, ..., 9] 都是合法的,直接返回这些数字。
    • 对于 n > 1,需要确保第一位不是零,且递归时每一位数字的差符合要求。
  • 排序:

    • 最终生成的数字需要排序后返回,因为题目要求从小到大输出结果。

解题代码

def solution(n: int, k: int) -> list:
    result = []
    
    # 递归函数来生成所有合法的数字
    def backtrack(current_number):
        # 如果当前数字长度等于n,保存该数字
        if len(current_number) == n:
            result.append(int("".join(map(str, current_number))))
            return
        
        # 获取上一个数字
        last_digit = current_number[-1]
        
        # 尝试添加新的数字,差的绝对值必须为k
        for diff in [-k, k]:
            new_digit = last_digit + diff
            if 0 <= new_digit <= 9:  # 新数字必须是有效的数字
                current_number.append(new_digit)
                backtrack(current_number)
                current_number.pop()  # 回溯
    
    # 处理n为1的特殊情况,返回0到9的所有数字
    if n == 1:
        return list(range(10))
    
    # 对每个可能的第一个数字(1-9)进行递归
    for first_digit in range(1, 10):
        backtrack([first_digit])
    
    # 返回所有生成的数字,并排序
    return sorted(result)

if __name__ == '__main__':
    print(solution(n=3, k=7) == [181, 292, 707, 818, 929])
    print(solution(n=2, k=1) == [10, 12, 21, 23, 32, 34, 43, 45, 54, 56, 65, 67, 76, 78, 87, 89, 98])
    print(solution(n=4, k=2) == [1313, 1353, 1357, 2020, 2024, 2420, 2424, 2464, 2468, 3131, 3135, 3531, 3535, 3575, 3579, 4202, 4242, 4246, 4642, 4646, 4686, 5313, 5353, 5357, 5753, 5757, 5797, 6420, 6424, 6464, 6468, 6864, 6868, 7531, 7535, 7575, 7579, 7975, 7979, 8642, 8646, 8686, 9753, 9757, 9797])

模块解释

  • backtrack(current_number) 函数:

    • 这是一个递归回溯的辅助函数,用于生成每一位数字。current_number 是当前生成的数字(以列表形式存储)。
    • 每次递归,先检查是否已经生成了长度为 n 的数字,如果是,就将该数字加入结果列表。
    • 然后,尝试通过对当前最后一位数字加上或减去 k 来生成新的数字,并递归调用 backtrack 继续生成剩余的数字。
    • 若某个数字无效(超出 0 到 9 的范围),则回溯。
  • 主函数 solution(n, k)

    • 处理 n = 1 的特殊情况,直接返回所有数字 [0, 1, 2, ..., 9]
    • 对于其他情况,首先从 1 到 9 生成可能的第一位数字,然后递归生成剩余的数字。
    • 最后,将所有生成的数字排序并返回。

结论

  • 时间复杂度:由于每一位的数字只能是 0 到 9 之间的数字,并且递归树的深度为 n,因此递归的时间复杂度大致为 O(10^n)。在每个递归调用中,我们最多有两种可能的选择(+k-k),所以总体的时间复杂度是指数级别的。

  • 空间复杂度:空间复杂度主要受递归栈的深度和存储结果的影响。递归栈的最大深度为 n,而结果列表中可能存储的数字个数最多为 10^n,因此空间复杂度也是 O(10^n)。

  • 这个问题能够通过回溯法有效求解,尤其是考虑了特殊情况 n = 1,并对数字生成过程进行了优化。