题号 96
题目描述
给定一个整数 n,求以 1 ... n 为节点组成的二叉搜索树有多少种?
示例:
输入: 3
输出: 5
解释:
给定 n = 3, 一共有 5 种不同结构的二叉搜索树:
1 3 3 2 1
\ / / / \ \
3 2 1 1 3 2
/ / \ \
2 1 2 3
概念解释——二叉搜索树
Binary Search Tree——二叉搜索树、二叉查找树、二叉排序树等等
是指这样的一种二叉树:
1 - 要么是一棵空树;
2 - 要么:
如果根节点的左子树不空,则左子树上所有节点的值小于根节点的值;如果根节点的右子树不空,则右子树上所有节点的值大于根节点的值;且它的左、右子树也分别是二叉排序树。
举个栗子:
解题思路——动态规划
Step1、2:描述问题的最优解结构特征、递归定义最优解值
按照动态规划的解决思路来说,对于给定的整数 n,由 1 .. n 为节点组成的二叉搜索树的个数应该和 n - 1 个节点的二叉搜索树个数、n - 2 个节点的二叉搜索树个数、…… 、有关。
设立 v[ n ] 表示由 1 .. n 为节点组成的二叉搜索树的个数:
n = 0 时,二叉搜索树为空树,因此 v[ 0 ] = 1;
n = 1 时,二叉搜索树只包含一个节点
O
/ \
左0 右0
因此 v[ 1 ] = 1;
n = 2 时,去除一个根节点,剩下的一个节点可能是左子树,也可能是右子树,如下所示:
O O
/ \ / \
左0 右1 左1 右0
因为零节点的二叉树只有一种,一节点的二叉搜索树也只有一种,
因此 v[ 2 ] = v[ 0 ] × v[ 1 ] + v[ 0 ] = 2
n = 3 时,去除一个根节点,剩下的两个节点的可能分布如下所示:
O O O
/ \ / \ / \
左0 右2 左1 右1 左2 右0
又因为两节点的二叉查找树有两种,一节点的二叉搜索树有一种,
因此 v[ 3 ] = v[ 0 ] × v[ 2] + v[ 1 ] × v[ 1 ] + v[ 2 ] × v[ 0 ] = 5
n = 4 时,去除一个根节点,剩下的三个节点的可能分布如下所示:
O O O O
/ \ / \ / \ / \
左0 右3 左1 右2 左2 右1 左3 右0
因此 v[ 4 ] = v[ 0 ] × v[ 3 ] + v[ 1 ] × v[ 2] + v[ 2 ] × v[ 1] + v[ 3 ] × v[ 0] = 14
n = 5 时,去除一个根节点,剩下的四个节点可能分布如下所示:
O O O O O
/ \ / \ / \ / \ / \
左0 右4 左1 右3 左2 右2 左3 右1 左4 右0
因此 v[ 5 ] = v[ 0 ] × v[ 4 ] + v[ 1 ] × v[ 3 ] + v[ 2 ] × v[ 2 ] + v[ 3 ] × v[1 ] + v[ 4 ] × v[ 0 ] = 42
……
对于 整数 n ,去除一个根节点,剩下的 n - 1 个节点可能分布如下所示:
O O O O O O
/ \ / \ / \ / \ …… / \ / \
左0 右n-1 左1 右n-2 左2 右n-3 左3 右n-4 左n-2 右1 左n-1 右0
因此 v[ n ] = v[ 0 ] × v[ n - 1 ] + v[ 1 ] × v[ n - 2 ] + v[ 2 ] × v[ n - 3] + …… + v[ n - 2 ] × v[ 1 ] + v[ n - 1 ] × v[ 0 ]
此问题的底是 v[ 0 ] = 1。
Step3、4:自底向上计算最优解值、输出最优解值
依次计算 v[ 1 ] 、v[ 2 ] 、…… v[ n ],问题的解就是终点 v[ n ] 的值。
代码实现如下:
class Solution {
public:
int numTrees(int n) {
vector<int> v(n+1); //数组v用于存放以各整数为节点的二叉搜索树的个数
v[0] = 1; //问题的底
for (int i = 1; i <= n; i++) { //依次计算v[1] v[2] …… v[n]的值
for (int j = 0; j <= i-1; j++) {
v[i] += v[j] * v[i - (j + 1)];
}
}
return v[n];
}
};
时间复杂度:O( n )
空间复杂度:O( n )
计算二叉搜索树个数的公式:
对于具有 n 个节点的二叉搜索树而言,其可能的不同种类的二叉搜索的树的个数为: