给定一个值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个节点;
//建立递归表达式:![图片说明](https://uploadfiles.nowcoder.com/files/20190221/417614_1550753757588_equation?tex=f(x)%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; } };