小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)