Dynamic Programming学习笔记 (32) - 所有可能的满二叉树 (力扣# 894)

230 阅读2分钟

本题出自力扣题库第894题。题面大意如下:

返回包含N个结点的所有可能满二叉树的列表。 满二叉树是二叉树中的一类,其中每个结点恰好有0个或2个子结点。

实例: N = 7

fivetrees.png

题解:

二叉树相关的DP问题一般使用递归以及DP用于存储计算过的结果。从题面出发,我们首先可以判断只有当N是奇数时,才有可能构造一个N个结点的满二叉树。由此出发我们可以定义一个递归方法,其输入参数为一个整数,代表树中所有节点的总数,其返回值是一个列表,包含所有符合满二叉树条件的树根节点。递归过程是一个单层循环,循环变量是根节点左边子树的节点数目,将节点总数减去左边子树的节点数目就可以得到右边子树的节点数目, 然后我们分别调用递归方法以得到所有可能的左边子树列表,以及所有可能的右边子树列表,其后使用一个双层循环将左右子树依次配对并加入用于返回的列表中。递归的边界情况是输入为1时,代表一个结点在左右两边都没有任何子节点。

我们使用一个一维DP数组来保存计算过的结果,用于后续的递归调用之中以避免重复计算。

Java代码如下:

import java.util.ArrayList;
import java.util.List;

class Solution {
    List<TreeNode>[] dp;

    public List<TreeNode> allPossibleFBT(int n) {
        if (n % 2 == 0) {
            return new ArrayList<>();
        }

        dp = new List[n + 1];
        return helper(n);
    }

    private List<TreeNode> helper(int n) {
        if (dp[n] != null) {
            return dp[n];
        }

        List<TreeNode> result = new ArrayList<>();

        if (n == 1) {
            TreeNode node = new TreeNode();
            result.add(node);
            return dp[n] = result;
        }

        for (int left = 1; left < n - 1; left += 2) {
            int right = n - 1 - left;

            List<TreeNode> leftTrees = helper(left);
            List<TreeNode> rightTrees = helper(right);

            for (TreeNode leftTree : leftTrees) {
                for (TreeNode rightTree: rightTrees) {
                    TreeNode root = new TreeNode();
                    root.left = leftTree;
                    root.right = rightTree;
                    result.add(root);
                }
            }
        }
        return dp[n] = result;
    }
}