AI刷题399:小M的好二叉树题目解析 | 豆包MarsCode AI刷题

122 阅读3分钟

小M的好二叉树:问题描述

小M定义了一种特殊的二叉树,称之为“好二叉树”。当且仅当这个二叉树中的所有节点的孩子数量为偶数(即每个节点要么没有孩子,要么有两个孩子),该二叉树才被称为好二叉树。

现在,小M想知道,给定 n 个节点,可以构成多少种不同形态的好二叉树?答案需要对 10^9 + 7 取模。

测试样例

样例1:

输入:n = 5
输出:2

样例2:

输入:n = 7
输出:5

样例3:

输入:n = 9
输出:14


解题思路:

问题分析

  • 首先需要理解"好二叉树"的定义:每个节点要么没有子节点,要么有两个子节点
  • 本质上是一个组合计数问题,需要计算满足条件的不同二叉树的数量
  • 注意到一个关键特征:由于每个非叶子节点必须有两个子节点,那么总节点数必须是奇数

解题方法选择

  • 这是一个典型的动态规划问题
  • 可以通过较小规模的子问题来构建更大规模的问题解
  • 需要考虑左右子树的所有可能组合

动态规划设计

  • 状态定义:dp[i]表示有i个节点时可以构成的好二叉树的数量
  • 初始状态:dp[1] = 1,因为单个节点是最基本的好二叉树
  • 状态转移:对于节点数为i的情况,需要枚举所有可能的左右子树组合

具体实现步骤:

a) 基础判断:

if n % 2 == 0:
    return 0  # 偶数节点无法构成好二叉树

b) 初始化动态规划数组:

dp = [0] * (n + 1)
dp[1] = 1  # 一个节点的情况
MOD = 10**9 + 7

c) 状态转移实现:

for i in range(3, n + 1, 2):  # 只考虑奇数节点的情况
    for j in range(1, i, 2):  # 枚举左子树节点数(必须是奇数)
        right = i - j - 1  # 计算右子树节点数
        if right % 2 == 1:  # 确保右子树节点数也是奇数
            dp[i] = (dp[i] + dp[j] * dp[right]) % MOD

细节说明

  • 外层循环从3开始,步长为2,确保只处理奇数节点的情况
  • 内层循环枚举左子树的节点数,同样只考虑奇数
  • 右子树节点数 = 总节点数 - 左子树节点数 - 1(根节点)
  • 使用乘法原理:左右子树的可能组合数相乘
  • 注意取模运算,防止数值溢出

时间复杂度分析

  • 外层循环:O(n)
  • 内层循环:O(n)
  • 总体时间复杂度:O(n²)

通过动态规划有效地解决问题,避免重复计算,同时保证所有可能的组合都被考虑到。

完整代码

def solution(n: int) -> int:
    # 如果节点数是偶数,则不能构成好二叉树
    if n % 2 == 0:
        return 0
        
    # dp[i]表示i个节点可以构成的好二叉树数量
    dp = [0] * (n + 1) 
    # 初始化,1个节点只有一种情况
    dp[1] = 1
    MOD = 10**9 + 7
    
    # 从3个节点开始递推
    for i in range(3, n + 1, 2):
        # 枚举左子树的节点个数,必须是奇数
        for j in range(1, i, 2):
            # 右子树节点个数也必须是奇数
            right = i - j - 1
            if right % 2 == 1:
                # 左右子树的组合数相乘
                dp[i] = (dp[i] + dp[j] * dp[right]) % MOD
                
    return dp[n]

if __name__ == '__main__':
    print(solution(5) == 2)
    print(solution(7) == 5) 
    print(solution(9) == 14)