题目
给定一个整数 ,求以
为节点组成的二叉搜索树有多少种?
示例:
输入: 3 输出: 5 解释: 给定 n = 3, 一共有 5 种不同结构的二叉搜索树:
1 3 3 2 1 \ / / / \ \ 3 2 1 1 3 2 / / \ \ 2 1 2 3
解题思路:动态规划
给定一个有序序列 ,为了构建出一棵二叉搜索树,我们可以遍历每个数字
,将该数字作为树根,将
序列作为左子树,将
序列作为右子树 。
在上述构建的过程中,由于根的值不同,因此我们能保证每棵二叉搜索树是唯一的。
定义两个函数:
1.  : 长度为  的序列能构成的不同二叉搜索树的个数。
1. : 以  为根、序列长度为  的不同二叉搜索树个数 。
可见,G(n)
是我们求解需要的函数。G(n)
可以从 F(i, n)
得到,而 F(i, n)
又会递归地依赖于G(n)
根据上述的思路,不同的二叉搜索树的总数 G(n)
,是对遍历所有 的
F(i, n)
之和。
对于边界情况,当序列长度为 1
(只有根)或为 0
(空树)时,只有一种情况,即:
给定序列 ,我们选择数字
作为根,则根为
的所有二叉搜索树的集合是左子树集合和右子树集合的笛卡尔积,对于笛卡尔积中的每个元素,加上根节点之后形成完整的二叉搜索树,如下图所示:
举例说明:
创建以 3
为根、长度为 7
的不同二叉搜索树,整个序列是 [1, 2, 3, 4, 5, 6, 7]
,我们需要从左子序列 [1, 2]
构建左子树,从右子序列 [4, 5, 6, 7]
构建右子树,然后将它们组合(即笛卡尔积)
。
我们将 [1,2]
构建不同左子树的数量表示为 G(2)
,将[4, 5, 6, 7]
构建不同右子树的数量表示为G(4)
注意到 G(n)
和序列的内容无关,只和序列的长度有关。 , 因此,可以得到
公式:
将公式 (1)
,公式(2)
结合,可以得到 G(n)
的递归表达式:

至此,我们从小到大计算 G函数
即可,因为 G(n)
的值依赖于 。
官方代码
class Solution {
public int numTrees(int n) {
int[] G = new int[n + 1];
G[0] = 1;
G[1] = 1;
for (int i = 2; i <= n; ++i) {
for (int j = 1; j <= i; ++j) {
G[i] += G[j - 1] * G[i - j];
}
}
return G[n];
}
}
解题思路:组合数学
事实上我们在方法一中推导出的 G(n)
函数的值在数学上被称为卡塔兰数 C_n
。卡塔兰数更便于计算的定义如下:
官方代码
class Solution {
public int numTrees(int n) {
// 提示:我们在这里需要用 long 类型防止计算过程中的溢出
long C = 1;
for (int i = 0; i < n; ++i) {
C = C * 2 * (2 * i + 1) / (i + 2);
}
return (int) C;
}
}