分析
1)用升序序列1…n构建一棵二叉搜索树,根节点任选一个,有n种选法。
2)但是选定了一个数作为根节点i(i∈[1,n]),那么由于二叉搜索树的特性,左子树只能用1…i-1构建,右子树只能用i+1…n构建。
3)设用1…i-1构建左子树有x种方法,用i+1…n构建右子树有y种方法,那么以i为根的二叉搜索树共x×y种。
4)把i依次取1,2,3…n的所得的结果累加起来便是最终结果。
需要注意的是,用元素个数相同的升序序列构造出的二叉搜索树的种类是相同的。比如用1,2,3构造出的二叉搜索树种数和用4,5,6构造出来的种数相同,因此我们只关心这个序列的个数,而不用关心它的大小。
递归的出口是,根节点是n=0或n=1
class Solution {
public int numTrees(int n) {
if (n == 0 || n == 1) {
return 1;
}
int ans = 0;
for (int i = 1; i <= n; i++) {
ans += numTrees(i - 1) * numTrees(n - i);
}
return ans;
}
}
注意上面的代码虽然可以通过,但是运行的额速度很慢,这是因为时间复杂度太高是指数级别,因为存在很多重复计算,比如说numTrees(2)会计算很多次。如果把递归函数修改成下面这样的,时间复杂度就会变低。
class Solution {
public int numTrees(int n) {
int[] record = new int[n + 1]; //空间换时间的思想
record[0] = 1;
return helper(n, record);
}
public int helper(int n, int[] record) {
if (n == 0 || n == 1) { // 递归出口
return 1;
}
if (record[n] > 0) { // 如果计算过了
return record[n]; // 提前返回结束递归求解,省时省空间
}
for (int i = 1; i <= n; i++) {
record[n] += helper(i - 1, record) * helper(n - i, record);
}
return record[n];
}