题目
思路
推一下就发现是卡特兰数了
或者直接画出来就能发现规律了
n=0和1肯定只有1种树
n=2有2种
n=3如上有5种
n=4的话有14种
其实看图上的例子我们就可以发现,固定一个根节点后,左右子树就是从剩下的结点里面选出来,如此递归就是一个二叉搜索树了
比如说固定了根节点为1,那么它的左子树是空的,而右子树相当于从2~n里面再构成一个二叉搜索树
固定了根节点为2,那么左子树只能是1,右子树相当于从3~n里面再构成一个二叉搜索树
固定根节点为3同理
…
固定根节点为n情况其实同固定根节点为1类似的的
第一次尝试
从上面分析我们可以写出初步的递归代码
class Solution {
public:
int numTrees(int n) {
if(n==0||n==1)return 1;
if(n==2)return 2;
int sum=0;
//依次选择1到n作为根节点
for(int i=0;i<n;i++){
//i作为根节点构成的树数量
sum += numTrees(i)*numTrees(n-i-1);
}
return sum;
}
};
运行结果
效率基本很低的
优化一下
class Solution {
public:
int numTrees(int n) {
if(n==0||n==1)return 1;
if(n==2)return 2;
int sum=0;
//依次选择1到n作为根节点
//*2是因为树的形状是对称的
for(int i=0;i<n/2;i++){
//i作为根节点构成的树数量
sum += numTrees(i)*numTrees(n-i-1)*2;
}
if(n%2!=0)sum += numTrees(n/2)*numTrees(n-1-n/2);
return sum;
}
};
运行结果,用时快多了,但是内存消耗还是很大,递归没办法
再尝试优化下
参考斐波那契数列里面的重复计算,我们用一个数组来存储结果
class Solution {
public:
int flag[1000]={0};
int numTrees(int n) {
if(flag[n]!=0)return flag[n];
if(n==0||n==1)return 1;
if(n==2)return 2;
int sum=0;
//依次选择1到n作为根节点
//*2是因为树的形状是对称的
for(int i=0;i<n/2;i++){
//i作为根节点构成的树数量
sum += numTrees(i)*numTrees(n-i-1)*2;
}
if(n%2!=0)sum += numTrees(n/2)*numTrees(n-1-n/2);
flag[n]=sum;
return sum;
}
};
运行结果 好像没有什么变化 😅…
官方题解
嗯。。是动态规划,
公式推得很巧妙,题解写得很清楚这里就不写了
https://leetcode-cn.com/problems/unique-binary-search-trees/solution/bu-tong-de-er-cha-sou-suo-shu-by-leetcode/
class Solution {
public:
int numTrees(int n) {
vector<int>G(n+1,0);
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];
}
};