先给出一种自己的做法:时间O(N^3),可以AC
直觉是n节点二叉搜索树的结构是通过n-1节点扩展出来的,且有一定规律。
花了15分钟,貌似找到了这个规律:
因为新插入的点一定是最大的,所以可以通过插缝的方式插入新节点n,但是有些是不满足二叉搜索树的排序关系,去掉后就是如图的逻辑关系。
class Solution {
public:
int numTrees(int n) {
int dp[n+1][n+1];
memset(dp,0,sizeof(dp));
dp[1][1] = 1;
for(int i=2;i<=n;i++){
for(int j=1;j<=i;j++){
for(int k=j-1;k<i;k++){
dp[i][j] += dp[i-1][k];
}
}
}
int ans = 0;
for(int i=1;i<=n;i++) ans+=dp[n][i];
return ans;
}
};
进阶版,O(N^2)的做法:
时间压缩一下,类似于快速幂的思路,
只用n节点信息可以扩展出n+1节点的信息,但是把1~n节点的信息都利用就可以加快计算。
一个n+1节点的二叉搜索树可以划分为,左边0个,根1个(值为1),右边n个;左边1个,根1个(值为2),右边n-1个;…以此类推。
把每个值都轮番作为根节点,由于二叉搜索树的特性,左子树上节点集合一定比根小,右子树节点集合一定比根大,所以相当于递归处理。
class Solution {
public:
int numTrees(int n) {
int dp[n+1];
memset(dp,0,sizeof(dp));
dp[0] = dp[1] = 1;
for(int i=2;i<=n;i++){
for(int j=0;j<i;j++){
dp[i] += dp[j]*dp[i-j-1];
}
}
return dp[n];
}
};
究极版,O(N)的做法:
根据进阶版递归公式
f(0) = f(1) = 1
f(n) = f(0)*f(n-1) + f(1)*f(n-2) + … + f(n-1)*f(0)
这个东西叫做Catalan数:
快速计算公式:
f(n) = C(2n,n) / (n+1)
class Solution {
public:
int numTrees(int n) {
long long ans = 1;
for(int i=1;i<=n;i++) ans = (n+i)*ans/i;
return ans/(n+1);
}
};
注:ans一定要用long long,否则会溢出,这里只能先乘n+i再除i,否则可能出现整数除法错误。