n个结点的二叉树的形状数目(动态规划)

一、题目描述

给定 n n n个结点,求二叉树的形状数目。

二、想法

已知最简单的情况是 n = 0 n=0 n=0 n = 1 n=1 n=1时, 树只有一种形状。


n = 2 n = 2 n=2 , 若左子树有0个节点,右子树则有1个节点,此时左右子树各只有一种情况。
由于左右子树的情况是可以对称的,所以 n = 2 n = 2 n=2共有 1 ∗ 2 1*2 12 种情况


n = 3 n=3 n=3
先保持左子树形状不变进行分析右子树的形状

  • 若左子树有0个节点,右子树则有 ( 3 − 1 ) (3-1) (31)个节点, 右子树的形状个数 等价于 n = 2 n = 2 n=2 时的解, 即2种情况。
  • 若左子树有1个节点,右子树则有 ( 3 − 2 ) (3-2) (32)个节点, 此时左右子树节点个数一致,右子树的形状个数 等价于 n = 1 n = 1 n=1 时的解,即只有1种情况。

保持右子树形状不变进行分析左子树的形状
… …由于左右子树的情况是可以对称的,不需要赘述了

综上所述, n = 3 n=3 n=3时,解为 2 × 2 + 1 × 1 = 5 2 \times 2 + 1\times1 = 5 2×2+1×1=5

… …


n = k n=k n=k
我们假设f(k)为节点数为n的二叉树形状个数
对于一颗有 n n n个结点的二叉树,现在我们假设左子树拥有 k k k个节点 ( 0 ≤ k ≤ n − 1 ) (0 \le k \le n - 1) (0kn1)

  • 保持其右子树不变,改变左子树形态,此时有 f ( k ) f(k) f(k)种可能( k ∈ N k \in N kN),
  • 然后,保持其左子树不变,改变其右子树形态,此时有 f ( n − k − 1 ) f(n-k-1) f(nk1)种可能

若左右子树节点个数相同, 即 n − k − 1 = k n - k-1 = k nk1=k时,则有 f ( n − k − 1 ) × f ( k ) f(n-k-1) \times f(k) f(nk1)×f(k)种可能否则,有 f ( n − k − 1 ) × f ( k ) × 2 f(n-k-1) \times f(k)\times 2 f(nk1)×f(k)×2种可能。(当左右子树节点数相同时,由于左右子树存在镜像对称的情况,因此不必×2)

三、代码实现

由于分治法会重复计算小问题的解,不妨用动态规划来实现

def get_cnt_of_tree_shape(n: int):
    l = [0] * (n + 1)
    # l[k]表示k个节点时二叉树形状个数
    l[0] = l[1] = 1
    for i in range(2, n + 1):
        # j取值上界为(i-1)//2,避免重复求解
        for j in range(0, (i-1)//2 + 1):
            if j == i - j - 1:
                l[i] += l[j] * l[i - j - 1]
            else:
                l[i] += l[j] * l[i - j - 1] * 2

    return l[n]

你可能感兴趣的:(#,水题,算法)