分治题目:不同的二叉搜索树 II

文章目录

题目

标题和出处

标题:不同的二叉搜索树 II

出处:95. 不同的二叉搜索树 II

难度

5 级

题目描述

要求

给定一个整数 n \texttt{n} n,返回所有由 n \texttt{n} n 个结点组成、结点值从 1 \texttt{1} 1 到 n \texttt{n} n 且结点值各不相同的不同二叉搜索树 。可以按任意顺序返回答案。

示例

示例 1:

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

输出: \[1,null,2,null,3,1,null,3,2,2,1,3,3,1,null,null,2,3,2,null,1] \texttt{\[1,null,2,null,3,1,null,3,2,2,1,3,3,1,null,null,2,3,2,null,1]} \[1,null,2,null,3,1,null,3,2,2,1,3,3,1,null,null,2,3,2,null,1]

示例 2:

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

输出: \[1] \texttt{\[1]} \[1]

数据范围

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

解法

思路和算法

为了构造二叉搜索树,需要确定二叉搜索树的结点值范围,并确定根结点的值,然后分别构造根结点的左子树和右子树。这是一个递归分治的过程。

由 n n n 个结点组成的二叉搜索树的结点值范围是 1 , n 1, n 1,n。用 rootVal \textit{rootVal} rootVal 表示根结点值,则 1 ≤ rootVal ≤ n 1 \le \textit{rootVal} \le n 1≤rootVal≤n,根结点的左子树的结点值范围是 1 , rootVal − 1 1, \\textit{rootVal} - 1 1,rootVal−1,根结点的右子树的结点值范围是 rootVal + 1 , n \\textit{rootVal} + 1, n rootVal+1,n。对于每个根结点值 rootVal \textit{rootVal} rootVal,其左子树和右子树分别可能有多种情况,根结点的左子树和右子树分别可以是任意一个符合要求的左子树和右子树。

start , end \\textit{start}, \\textit{end} start,end 表示二叉搜索树的结点值范围。分治的终止条件是结点值范围为空,此时符合结点值范围的只有空二叉搜索树 null \text{null} null。当结点值范围不为空时,分别将每个结点值作为根结点值,然后递归地构造左子树和右子树。

当根结点值为 rootVal \textit{rootVal} rootVal 时,用 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 分别作为根结点的左子树和右子树,即可得到一个符合要求的二叉搜索树。特别地,当 rootVal = 1 \textit{rootVal} = 1 rootVal=1 时必有 leftSubtree = null \textit{leftSubtree} = \text{null} leftSubtree=null,当 rootVal = n \textit{rootVal} = n rootVal=n 时必有 rightSubtree = null \textit{rightSubtree} = \text{null} rightSubtree=null。

代码

java 复制代码
class Solution {
    public List<TreeNode> generateTrees(int n) {
        if (n == 0) {
            return new ArrayList<TreeNode>();
        } else {
            return generateTrees(1, n);
        }
    }

    public List<TreeNode> generateTrees(int start, int end) {
        List<TreeNode> trees = new ArrayList<TreeNode>();
        if (start > end) {
            trees.add(null);
        } else {
            for (int rootVal = start; rootVal <= end; rootVal++) {
                List<TreeNode> leftSubtrees = generateTrees(start, rootVal - 1);
                List<TreeNode> rightSubtrees = generateTrees(rootVal + 1, end);
                for (TreeNode leftSubtree : leftSubtrees) {
                    for (TreeNode rightSubtree : rightSubtrees) {
                        TreeNode root = new TreeNode(rootVal);
                        root.left = leftSubtree;
                        root.right = rightSubtree;
                        trees.add(root);
                    }
                }
            }
        }
        return trees;
    }
}

复杂度分析

  • 时间复杂度: O ( 4 n n ) O(\dfrac{4^n}{\sqrt{n}}) O(n 4n),其中 n n n 是二叉搜索树的结点数。由 n n n 个结点组成的二叉搜索树的数量是第 n n n 个卡特兰数 C 2 n n n + 1 \dfrac{C_{2n}^n}{n + 1} n+1C2nn,其渐进上界为 O ( 4 n n n ) O(\dfrac{4^n}{n \sqrt{n}}) O(nn 4n),对于每个二叉搜索树需要 O ( n ) O(n) O(n) 的时间生成和添加到答案中,因此时间复杂度是 O ( 4 n n ) O(\dfrac{4^n}{\sqrt{n}}) O(n 4n)。

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

后记

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

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

相关推荐
wabs66612 小时前
关于二叉树【力扣199.二叉树的右视图的思考】
数据结构·c++·算法·leetcode·二叉树·层序遍历
hope_wisdom12 小时前
C/C++数据结构之二叉树的遍历
c语言·数据结构·c++·二叉树·深度优先·广度优先
wabs6662 天前
关于二叉树【力扣107.二叉树的层序遍历II的思考】
数据结构·c++·算法·leetcode·二叉树·层序遍历
LuminousCPP4 天前
数据结构-二叉树(六):BFS层序遍历与完全二叉树判断|复用链式队列 + (N_0=N_2+1) 性质证明
c语言·数据结构·笔记·算法·二叉树·宽度优先
lvwangshu6 天前
分治:序列分治(CDQ)与点分治
算法·分治
_Narcissus_9 天前
分治&递归
数据结构·c++·笔记·算法·leetcode·递归·分治
LuminousCPP10 天前
数据结构-二叉树(五):查找、销毁与前序序列建树
c语言·数据结构·笔记·二叉树
林森lsjs12 天前
零基础吃透二叉树:定义、遍历与高频算法 —数据结构柒
java·开发语言·数据结构·算法·二叉树
wabs66612 天前
关于二叉树【力扣144.二叉树的前序遍历的思考】
数据结构·c++·算法·leetcode·二叉树
LuminousCPP13 天前
数据结构‑二叉树(二):二叉堆从零实现|Heap结构设计 + 建堆优化 + 堆排序深度解析
c语言·数据结构·笔记·二叉树