首页 简历|笔试面试
您现在的位置:百分网 > 职场 > 笔试面试 > LeetCode

95.不同的二叉搜索树II

  • 25年9月4日 发布
  • 131.65KB 共5页
95.不同的二叉搜索树II95.不同的二叉搜索树II95.不同的二叉搜索树II95.不同的二叉搜索树II95.不同的二叉搜索树II

95. 不同的二叉搜索树 II

题意

给你一个整数 n ,请你生成并返回所有由 n 个节点组成且节点值从 1 到 n 互不相同的不

同 二叉搜索树 。可以按 任意顺序 返回答案。

难度

中等

示例

示例 1:

095.%E4%B8%8D%E5%90%8C%E7%9A%84%E4%BA%8C%E5%8F%89%E6%90%9C%E7

%B4%A2%E6%A0%91II-20231030163552.png

输入:n = 3

输出:[[1,null,2,null,3],[1,null,3,2],[2,1,3],[3,1,null,null,2],[3,2,null,1]]

示例 2:

输入:n = 1

输出:[[1]]

分析

首先我们来了解一下什么是二叉搜索树。

二叉搜索树是一种二叉树的树形数据结构,其定义如下:

1. 空树是二叉搜索树。

2. 若二叉搜索树的左子树不为空,则其左子树上所有点的附加权值均小于其根节点

的值。

3. 若二叉搜索树的右子树不为空,则其右子树上所有点的附加权值均大于其根节点

的值。

4. 二叉搜索树的左右子树均为二叉搜索树。

——引自[二叉搜索树 & 平衡树 - OI

Wiki](https://oiwiki.org/ds/bst/)

简单来说,二叉搜索树需要满足:每个节点的左子树都比自己小、右子树都比自己大。

最简单的情况,一条有序的链表,就算是一个二叉搜索树。例如:

095.%E4%B8%8D%E5%90%8C%E7%9A%84%E4%BA%8C%E5%8F%89%E6%90%9C%E7

%B4%A2%E6%A0%91II-20231030164012.png

好,接下来我们要知道节点个数为 n 的二叉搜索树的个数是多少。

假设 n 个节点的 二叉搜索树 的个数为$ G(n) , f(i) 为 以 i $作为根节点的 n 个节点的二叉

搜索树个数。

我们可以得到这样的式子:

$ G(n) = f(1) + f(2) + + f(n) $

并且当我们分析$ f(i) $的时候,还可以得到这样子一个式子:

$ f(i) = G(i-1) * G(n - i) $

因为我们知道总数为 n 的话,当前节点的两个子树,必然是大小为 i-1 和 n-i 的两棵二叉

搜索树。

综合一下两个公式我们能得到如下式子:

$ G(n) = G(0) * G(n - 1) + G(1) * G(n - 2) + + G(n - 1) * G(0) $

而这,其实就是卡特兰数。

简单来说,卡特兰数就是在某些特定限制条件下,可能的组合或排列的数量。

举一个常见的例子:你有一堆左括号和右括号,要组成一个正确的括号组合(即每个左括

号都有一个与之对应的右括号),那么可能的组合数就是一个卡特兰数。

好,我们选择一个数作为根节点,然后分配左右子树,最后按照左右子树的大小继续递归

建树即可得到答案。

/**

* Definition for a binary tree node.

* public class TreeNode {

* int val;

* TreeNode left;

* TreeNode right;

* TreeNode() {}

* TreeNode(int val) { this.val = val; }

* TreeNode(int val, TreeNode left, TreeNode right) {

* this.val = val;

* this.left = left;

* this.right = right;

* }

*}

*/

class Solution {

// 主方法,接收一个整数 n 作为参数

public List<TreeNode> generateTrees(int n) {

// 当 n 为 0 时,没有可生成的树,直接返回一个空的列表

if (n == 0) {

return new LinkedList<TreeNode>();

}

// 调用辅助方法 generateTrees,生成从 1 到 n 的所有可能的二叉搜索树

return generateTrees(1, n);

}

// 辅助方法,接收两个整数作为参数,表示生成树的值的范围

public List<TreeNode> generateTrees(int start, int end) {

List<TreeNode> trees = new LinkedList<TreeNode>(); // 用于存储生成的所有

树的列表

// 当开始值大于结束值时,表示该区间无有效数字,返回一个包含 null 的列表

if (start > end) {

trees.add(null);

return trees;

}

// 遍历从 start 到 end 的所有数字,将每一个数字作为根节点

for (int i = start; i <= end; i++) {

// 生成所有可能的左子树

List<TreeNode> leftTrees = generateTrees(start, i - 1);

// 生成所有可能的右子树

List<TreeNode> rightTrees = generateTrees(i + 1, end);

// 对于每一颗左子树和右子树的组合,创建一个新的树

for (TreeNode left : leftTrees) {

for (TreeNode right : rightTrees) {

TreeNode newTree = new TreeNode(i); // 创建一个新的树的根节点

newTree.left = left; // 将当前左子树连接到新树的左侧

newTree.right = right; // 将当前右子树连接到新树的右侧

trees.add(newTree); // 将新生成的树添加到 trees 列表中

}

}

}

return trees; // 返回生成的所有树的列表

}

}

当输入 n = 3 时,我们需要构造所有可能的二叉搜索树,其中树的节点值从 1 到 3。

遍历每个数字 i 从 1 到 3,将其视为根节点,并递归地为其左右子树生成所有可能的结

构。

让我们模拟题解的过程:

1. i = 1 作为根节点:

– 左子树: 无

– 右子树: 2,3

• 当 2 作为根节点:左子树无,右子树有 3

• 当 3 作为根节点:左子树有 2,右子树无

结果:

1 1

2 3

/

3 2

2. i = 2 作为根节点:

– 左子树: 1

– 右子树: 3

结果:

2

/

1 3

3. i = 3 作为根节点:

– 左子树: 1,2

• 当 1 作为根节点:右子树有 2,左子树无

• 当 2 作为根节点:左子树有 1,右子树无

– 右子树: 无

结果:

3 3

/ /

1 2

/

2 1

合并上述所有的结果,我们可以得到 5 个不同的二叉搜索树。

来看一下题解效率:

095.%E4%B8%8D%E5%90%8C%E7%9A%84%E4%BA%8C%E5%8F%89%E6%90%9C%E7

%B4%A2%E6%A0%91II-20231030171013.png

这题的核心思想是采用递归的方法来生成所有可能的二叉搜索树。

1. 递归基础:

当给定的范围内没有数字时(即 start > end),这意味着此处的子树为 null,所以

我们应该返回一个列表,其中只包含一个 null 元素。

2. 中间节点选择:

对于范围内的每个数字 i,我们都尝试将其作为当前树的根节点。也就是说,我们认

为每个数字都有可能作为根节点。

3. 左、右子树的生成:

– 对于根节点 i,它的左子树应该由 start 到 i - 1 的所有可能树组成,而右子树

应该由 i + 1 到 end 的所有可能树组成。

– 使用递归,我们可以得到两个列表:leftTrees 和 rightTrees,分别代表所有

可能的左和右子树。

– 然后,我们组合这些左子树和右子树,形成所有可能的树结构。

总结

本题通过总结公式从而得出构建二叉树的方法,这个套路其实也比较普遍,因为它和卡特

兰数有关,建议在刷题之余,有意识地去总结对应类别的题目,往往可以给我们带来意想

不到的收获。

力扣链接:https://leetcode.cn/problems/unique-binary-search-trees-ii/

开通会员 本次下载免费

所有资料全部免费下载! 推荐用户付费下载获取返佣积分! 积分可以兑换商品!
一键复制 下载文档 联系客服