分治题目:所有可能的真二叉树

文章目录

题目

标题和出处

标题:所有可能的真二叉树

出处:894. 所有可能的真二叉树

难度

5 级

题目描述

要求

给定一个整数 n \texttt{n} n,返回所有含 n \texttt{n} n 个结点的真二叉树 的列表。答案中每个树的每个结点值都必须是 0 \texttt{0} 0。

答案的每个元素都是一个真二叉树的根结点。可以按任意顺序返回答案。

真二叉树 是一类二叉树,树中每个结点恰好有 0 \texttt{0} 0 或 2 \texttt{2} 2 个子结点。

示例

示例 1:

输入: n = 7 \texttt{n = 7} n = 7

输出: \[0,0,0,null,null,0,0,null,null,0,0,0,0,0,null,null,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,null,null,null,null,0,0,0,0,0,0,0,null,null,0,0] \texttt{\[0,0,0,null,null,0,0,null,null,0,0,0,0,0,null,null,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,null,null,null,null,0,0,0,0,0,0,0,null,null,0,0]} \[0,0,0,null,null,0,0,null,null,0,0,0,0,0,null,null,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,null,null,null,null,0,0,0,0,0,0,0,null,null,0,0]

示例 2:

输入: n = 3 \texttt{n = 3} n = 3

输出: \[0,0,0] \texttt{\[0,0,0]} \[0,0,0]

数据范围

  • 1 ≤ n ≤ 20 \texttt{1} \le \texttt{n} \le \texttt{20} 1≤n≤20

解法

思路和算法

根据真二叉树的定义,真二叉树中的每个结点的子结点数是 0 0 0 或 2 2 2。真二叉树中的结点数是奇数,可以使用数学归纳法证明。

  1. 当二叉树中只有一个结点时,唯一的结点是根结点,没有子结点,因此是真二叉树,此时真二叉树中的结点数是奇数。

  2. 当真二叉树中有 m m m 个结点时,将其中一个叶结点增加两个子结点之后仍为真二叉树,新的真二叉树中有 m + 2 m + 2 m+2 个结点,当 m m m 是奇数时, m + 2 m + 2 m+2 也是奇数。

由于真二叉树中的结点数一定是奇数,因此当 n n n 是偶数时不存在真二叉树,返回空列表。

当 n n n 是奇数时, n n n 个结点的真二叉树满足左子树和右子树的结点数都是奇数且左子树和右子树的结点数之和是 n − 1 n - 1 n−1,分别构造根结点的左子树和右子树,即可得到 n n n 个结点的真二叉树。这是一个递归分治的过程。

分治的终止条件是 n n n 是偶数和 n = 1 n = 1 n=1,当 n n n 是偶数时不存在真二叉树,当 n = 1 n = 1 n=1 时只有一个结点的二叉树是真二叉树。当 n > 1 n > 1 n>1 且 n n n 是奇数时,遍历左子树和右子树的结点数,然后递归地构造左子树和右子树。

确定左子树和右子树的结点数之后,用 leftSubtrees \textit{leftSubtrees} leftSubtrees 和 rightSubtrees \textit{rightSubtrees} rightSubtrees 分别表示符合要求的左子树列表和右子树列表,对于任意 leftSubtree ∈ leftSubtrees \textit{leftSubtree} \in \textit{leftSubtrees} leftSubtree∈leftSubtrees 和 rightSubtree ∈ rightSubtrees \textit{rightSubtree} \in \textit{rightSubtrees} rightSubtree∈rightSubtrees,将 leftSubtree \textit{leftSubtree} leftSubtree 和 rightSubtree \textit{rightSubtree} rightSubtree 分别作为根结点的左子树和右子树,即可得到一个真二叉树。

代码

java 复制代码
class Solution {
    public List<TreeNode> allPossibleFBT(int n) {
        List<TreeNode> fullBinaryTrees = new ArrayList<TreeNode>();
        if (n % 2 == 0) {
            return fullBinaryTrees;
        }
        if (n == 1) {
            fullBinaryTrees.add(new TreeNode(0));
            return fullBinaryTrees;
        }
        for (int left = 1, right = n - 2; left < n; left++, right--) {
            List<TreeNode> leftSubtrees = allPossibleFBT(left);
            List<TreeNode> rightSubtrees = allPossibleFBT(right);
            for (TreeNode leftSubtree : leftSubtrees) {
                for (TreeNode rightSubtree : rightSubtrees) {
                    TreeNode root = new TreeNode(0, leftSubtree, rightSubtree);
                    fullBinaryTrees.add(root);
                }
            }
        }
        return fullBinaryTrees;
    }
}

复杂度分析

  • 时间复杂度: O ( 2 n n ) O(\dfrac{2^n}{\sqrt{n}}) O(n 2n),其中 n n n 是真二叉树的结点数。只有当 n n n 是奇数时才存在真二叉树,记 n = 2 k + 1 n = 2k + 1 n=2k+1,由 n n n 个结点组成的真二叉树的数量是第 k k k 个卡特兰数 C ( 2 k , k ) k + 1 \dfrac{C(2k, k)}{k + 1} k+1C(2k,k),其渐进上界为 O ( 4 k k k ) = O ( 2 n n n ) O(\dfrac{4^k}{k \sqrt{k}}) = O(\dfrac{2^n}{n \sqrt{n}}) O(kk 4k)=O(nn 2n),对于每个真二叉树需要 O ( n ) O(n) O(n) 的时间生成和添加到答案中,因此时间复杂度是 O ( 2 n n ) O(\dfrac{2^n}{\sqrt{n}}) O(n 2n)。

  • 空间复杂度: O ( n ) O(n) O(n),其中 n n n 是真二叉树的结点数。递归调用栈需要 O ( n ) O(n) O(n) 的空间。注意返回值不计入空间复杂度。

后记

此处提供的解法为分治。由于同一个结点值范围的子树可能被多次计算,因此可以使用记忆化。这道题中的结点数不超过 20 20 20,因此使用记忆化并不能显著提升性能。

读者可以自行尝试使用记忆化的实现。

相关推荐
神威难绷泪2 小时前
数据结构:二叉树
数据结构·二叉树
伟大的车尔尼21 小时前
分治题目:不同的二叉搜索树 II
二叉树·二叉搜索树·分治
Tyler_TXZ2 天前
C++C语言之——二叉树
c语言·开发语言·数据结构·c++·二叉树
love_muming8 天前
二叉树操作全解析:从递归到层序遍历
java·数据结构·算法·二叉树
-dzk-10 天前
【二叉树】LC 101.对称二叉树
算法·二叉树
bnmoel16 天前
数据结构深度剖析二叉树・下篇:链式结构的实现 ,遍历方法全解析
c语言·数据结构·算法·二叉树·
星马梦缘16 天前
算法设计导论 课堂笔记
算法·动态规划·分治·时间复杂度·最优二叉树
stolentime24 天前
SP8549 MAIN75 - BST again题解
c++·算法·二叉树·深度优先·图论·记忆化搜索·组合数学
bu_shuo1 个月前
计算机二级公共知识学习-树和二叉树
二叉树··中序遍历·后序遍历·计算机二级·先序遍历