题目
给定一个整数 n,求以 1 ... n 为节点组成的二叉搜索树有多少种?
示例:
输入: 3
输出: 5
解释:
给定 n = 3, 一共有 5 种不同结构的二叉搜索树:
1 3 3 2 1
\ / / / \ \
3 2 1 1 3 2
/ / \ \
2 1 2 3
二叉搜索树
- 节点的左子树只包含小于当前节点的数。
- 节点的右子树只包含大于当前节点的数。
- 所有左子树和右子树自身必须也是二叉搜索树。
思路1
递归。具体见代码。这种思路很慢。这种思路可以返回所有的二叉搜索树(LeetCode97)。
代码1
class Solution {
public int numTrees(int n) {
int sum=0;
// 每个点作为根节点时的总数,加起来
for(int i=1;i<=n;i++){
sum+=w(1,i,n);
}
return sum;
}
// b: begin 开始索引
// e:end 结束索引
// m:根节点
public int w(int b,int m,int e){
//递归结束条件
if(b==m && m==e){
return 1;
}
// 根节点左边的数量
int leftsum=0;
for(int i=b;i<=m-1;i++){
// 递归。每个点作为根节点时的总数,加起来
leftsum+=w(b,i,m-1);
}
// 根节点右边的数量
int rightsum=0;
for(int j=m+1;j<=e;j++){
// 递归。每个点作为根节点时的总数,加起来
rightsum+=w(m+1,j,e);
}
// 总数=左边数量*右边数量
int sum=0;
if(leftsum==0 && rightsum==0){
sum=0;
}else if(leftsum==0 && rightsum!=0){
sum=rightsum;
}else if(leftsum!=0 && rightsum==0){
sum=leftsum;
}else{
sum=rightsum*leftsum;
}
return sum;
}
}
思路2
动态规划。关键思路:选好根节点后,该根节点下总数=根节点左边总数*根节点右边总数。
n = 2的:
dp[2] = dp[0] * dp[1] (1为根的情况)
+ dp[1] * dp[0] (2为根的情况)
n = 3 的情况:
dp[3] = dp[0] * dp[2] (1为根的情况)
+ dp[1] * dp[1] (2为根的情况)
+ dp[2] * dp[0] (3为根的情况)
代码2
class Solution {
public int numTrees(int n) {
if (n == 0)return 0;
if (n == 1) return 1;
int[] nums = new int[n+1];
// 小技巧:不能设为0,应该设为1
nums[0] = 1;
nums[1] = 1;
for (int i = 2; i <= n; i++) {
for (int j = 0; j < i; j++) {
nums[i] = nums[i] + nums[j] * nums[i-1-j];
}
}
return nums[n];
}
}
总结
动态规划。