给定一个值n,能构建出多少不同的值包含1...n的二叉搜索树(BST)?
例如
给定 n = 3, 有五种不同的二叉搜索树(BST)
public static int numTrees(int n){
if(n<0){
return -1;
}
int[] dp=new int[n+1];
dp[0]=1;
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];
}
我设dp[i]表示共有i个节点时,能产生的BST树的个数 n == 0 时,空树的个数必然为1,因此dp[0] = 1 n == 1 时,只有1这个根节点,数量也为1,因此dp[1] = 1 n == 2时,有两种构造方法,如下图所示: 加载中... 因此,dp[2] = dp[0] * dp[1] + dp[1] * dp[0] n == 3时,构造方法如题目给的示例所示,dp[3] = dp[0] * dp[2] + dp[1] * dp[1] + dp[2] * dp[0] 同时,当根节点元素为 1, 2, 3, 4, 5, ..., i, ..., n时,基于以下原则的BST树具有唯一性: 以i为根节点时,其左子树构成为[0,...,i-1],其右子树构成为[i+1,...,n]构成 因此,dp[i] = sigma(dp[0...k] * dp[k+1...i]) 0 <= k < i - 1
int numTrees(int n) {
//参考卡特兰数
//递推公式:令h(0)=1,h(1)=1,catalan数满足递推式[2] :
//h(n)= h(0)*h(n-1)+h(1)*h(n-2) + ... + h(n-1)*h(0) (n>=2)
//例如:h(2)=h(0)*h(1)+h(1)*h(0)=1*1+1*1=2
//h(3)=h(0)*h(2)+h(1)*h(1)+h(2)*h(0)=1*2+1*1+2*1=5
//另类递推式[3] :
//h(n)=h(n-1)*(4*n-2)/(n+1);
int dp[n];
dp[0]=1;
for(int i=1;i<=n;i++)
dp[i]=dp[i-1]*(4*i-2)/(i+1);
return dp[n];
}
import java.util.*;
public class Solution {
/**
*
* @param n int整型
* @return int整型
*/
public int numTrees (int n) {
// write code here
int[] dp = new int[n+1]; //i 能构成的二叉搜索树数目
dp[0]=1;dp[1]=1; //初始化,注意 0 的时候赋值 1
for(int i=2;i<n+1;i++){ //从小到大遍历
for(int j=1;j<=i;j++){
dp[i]+=dp[i-j]*dp[j-1]; //状态转移方程:左子树所有情况*右子树所有情况
}
}
return dp[n];
}
} 时间O(n2)
public class Solution {
public int numTrees(int n) {
int[] G = new int[n + 1];
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];
}
}
public class Solution {
public int numTrees(int n) {
long Catalan = 1;
for(int i = 0; i < n; i++){
Catalan = 2*(2*i+1)*Catalan/(i+2);
}
return (int)Catalan;
}
}
public int numTrees(int n) {
if(n<=1)
return 1;
int res = 0;
for(int i=1;i<=n;i++){
res+=numTrees(i-1)*numTrees(n-i);
}
return res;
} public int dp(int n){
int[] dp = new int[n+1];
dp[0] = 1;
dp[1] = 1;
for(int i=2;i<=n;i++){
for(int j=1;j<=i;j++){
dp[i]+=dp[j-1]*dp[i-j];
}
}
return dp[n];
}
/*
动态规划。
dp[i]表示i个节点时,种类数。
二叉排序树的中序遍历是递增序列。
取不同根节点时,肯定为不同的树,该大类树的种树等于左子树种类*右子数种类。
*/
public class Solution {
public int numTrees(int n) {
int[] dp = new int[n + 1];
dp[1] = 1;
dp[0] = 1;
for (int m = 2; m <= n; m++)
for (int i = 1; i <= m; i++) {
dp[m] += dp[i - 1] * dp[m - i];
}
return dp[n];
}
}
public class Solution {
public int numTrees(int n) {
int[] store=new int[n+1];
store[1]=1;
for(int i=2;i<=n;i++) {
for(int j=1;j<=i;j++) {
if(j==1||j==i) store[i]+=store[i-1];
else store[i]+=store[i-j]*store[j-1];
}
}
return store[n];
}
} //动态规划思想:维护一个数组来存储n = i时候的BST的个数。这样的话给定一个数n,可以分为多种情况,
//若根节点是n,那个它就没有右子树,左子树最有n-1个节点,若根节点为i,那么左子树有i-1个节点,右子树有i+1个节点;
//建立递归表达式:%3D%5Cleft%5C%7B1%EF%BC%8C%E5%A6%82%E6%9E%9Cn%20%3D%3D%201%3B%202%2C%E5%A6%82%E6%9E%9Cn%3D%3D2%3B%20f(x-1)%2Bf(1)*f(x-2)%2B...%2Bf(x-1)%5Cright%5C%7D "图片标题")
class Solution {
public:
int numTrees(int n) {
if (n == 0)
return 0;
vector<int> vec{ 1,2 };
for (int i = 3; i <= n ;i++)
{
int count = 0;
for (int j = 0; j < i; j++)
{
if (j == 0 || j == i-1)
{
count += vec[i - 2];
continue;
}
count += vec[j-1] * vec[i - j - 2];
}
vec.push_back(count);
}
return vec[n - 1];
}
};
//递归法
class Solution {
public:
int numTrees(int n) {
if(n==0)
return 1;
int res = 0;
for(int i=0; i<n; ++i){
res += numTrees(i)*numTrees(n-1-i);
}
return res;
}
};
//非递归,创建一个数组,第i个值等于前i-1个数首尾相乘,然后向中间逼近
class Solution {
public:
int numTrees(int n) {
int* num = new int[n+1];
num[0] = 1;
for(int i=1; i<n+1; ++i){
num[i] = 0;
for(int j=0; j<i; ++j){
num[i] += num[j]*num[i-1-j];
}
}
return num[n];
}
}; public class Solution {
public int numTrees(int n) {
if(n < 0)
return -1;
int[] dp = new int[n + 1];
dp[0] = 1;
dp[1] = 1;
for(int i = 2; i <= n; i++){ //i为当前节点值,
for(int j = 0; j < i; j++){
dp[i] += dp[j] * dp[i - j - 1];//dp[j]左子树种数,dp[i - j - 1]右子树种数
}
}
return dp[n];
}
}
//BST树:以i为根节点的树,其左子树由[0, i-1]构成, 其右子树由[i+1, n]构成。 //动态规划状态为count[n],count[n]表示到正整数i为止的BST个数 ; public class Solution { public int numTrees(int n) { if (n < 0) { return -1; } int[] count = new int[n + 1]; count[0] = 1; for (int i = 1; i < n + 1; ++i) { for (int j = 0; j < i; ++j) { count[i] += count[j] * count[i - j - 1]; } } return count[n]; } }
class Solution {
public:
int numTrees(int n) {
//bool odd; //是否是奇数的标示
/*
vector<int> array;
array.push_back(1); //f0=1
array.push_back(1); //f1=1
array.push_back(2); //f2=2
array.push_back(5); //f3=5
for(int i=4;i<=n;i++){
if((i&1)==1){
//odd = true;
int half = i>>1;
int sum=0;
int j;
for(j=0;j<half;j++){
sum += array[j]*array[i-1-j];
}
sum *= 2;
sum += array[half]*array[half];
array.push_back(sum); //fn
}else{
//odd = false;
int half = i>>1;
int sum=0;
for(int j=0;j<half;j++){
sum += array[j]*array[i-1-j];
}
array.push_back(2*sum); //fn
}
}
return array[n];
*/
//方式二:如果了解卡特兰数列的话,其实二叉搜索树的形态结构树就是一个卡特兰数列
//直接套用公式计算 h(n)=C(2n,n)/(n+1)={2n(2n-1)(2n-2)...(n+2)}/{n(n-1)(n-2)...1} 组合数需要计算阶乘
//可以采用递推公式 h(n)=(4n-2)/(n+1)*h(n-1) 来计算
int fn_1 = 1,fn;
if(n==1)return fn_1;
for(int i=2;i<=n;i++){
fn = (float)(4*i-2)/(i+1)*fn_1;
fn_1 = fn;
}
return fn;
}
};