- Unique Binary Search Trees
中文English
Given n, how many structurally unique BSTs (binary search trees) that store values 1…n?
Example
Example 1:
Input:n = 3,
Output: 5
Explanation:there are a total of 5 unique BST’s.
1 3 3 2 1
\ / / / \
3 2 1 1 3 2
/ / \
2 1 2 3
解法1:
**思路:以i为根节点的BST树,其左子树由[0, i-1]构成, 其右子树由[i+1, n]构成。
下面参考了:
http://bangbingsyb.blogspot.com/2014/11/leetcode-unique-binary-search-trees-i-ii.html
定义f(n)为unique BST的数量,以n = 3为例:
构造的BST的根节点可以取{1, 2, 3}中的任一数字。
如以1为节点,则left subtree只能有0个节点,而right subtree有2, 3两个节点。所以left/right subtree一共的combination数量为:f(0) * f(2) = 2
以2为节点,则left subtree只能为1,right subtree只能为2:f(1) * f(1) = 1
以3为节点,则left subtree有1, 2两个节点,right subtree有0个节点:f(2)*f(0) = 2
总结规律:
f(0) = 1
f(n) = f(0)*f(n-1) + f(1)*f(n-2) + … + f(n-2)*f(1) + f(n-1)*f(0)
这些数目加起来即Catalan数。**
解法1即直接用catalan数公式求。f(n) = C(2n, n)/(n + 1)。
简化后可得f(n)=(n+2)(n+3)…(2n-1)/(34…(n-1))。
代码如下:
class Solution {
public:
/**
* @param n: An integer
* @return: An integer
*/
int numTrees(int n) {
if (n <= 1) return 1;
if (n == 2) return 2;
double numerator = 1;
double denominator = 1;
int n2 = n * 2;
for (int i = n + 2; i < n2; ++i) {
numerator *= i;
}
for (int i = 3; i < n; ++i) {
denominator *= i;
}
return (int)ceil(numerator / denominator);
}
};
解法2:DP
代码如下:
class Solution {
public:
/**
* @param n: An integer
* @return: An integer
*/
int numTrees(int n) {
if (n <= 1) return 1;
vector<int> dp(n + 1, 0);
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];
}
};
解法3:同样是DP。
但是我们从等价的出栈顺序数问题来考虑。
dp[i][j]表示栈外面有i个数要入栈,栈内有j个数的情况。
代码如下:
class Solution {
public:
/**
* @param n: An integer
* @return: An integer
*/
int numTrees(int n) {
if (n <= 1) return 1;
//consider it as an equivalent # of out-stock sequence problem
vector<vector<int>> dp(n + 1, vector<int>(n + 1, 0)); //dp[out][in]
for (int i = 0; i <= n; ++i) dp[0][i] = 1;
for (int i = 0; i <= n; ++i) {
if (i > 0) dp[i][0] = dp[i - 1][1];
for (int j = 0; j <= n - i; ++j) {
if (i > 0 && j > 0)
dp[i][j] = dp[i - 1][j + 1] + dp[i][j - 1];
}
}
return dp[n][0];
}
};
解法4:解法3的递归版。
class Solution {
public:
/**
* @param n: An integer
* @return: An integer
*/
int numTrees(int n) {
return catalan(n, 0);
}
private:
int catalan(int outside, int inside) {
if (outside == 0) return 1; //stack full
if (inside == 0) return catalan(outside - 1, 1); //stack empty
return catalan(outside - 1, inside + 1) + catalan(outside, inside - 1);
}
};
注意,上面的版本在n大的时候会超时,应该用下面的memorization版本。
class Solution {
public:
/**
* @param n: An integer
* @return: An integer
*/
int numTrees(int n) {
catalanNums.resize(n + 1, vector<int>(n + 1, -1));
for (int i = 0; i <= n; ++i) {
for (int j = 0; j <= n - i; ++j) {
if (catalanNums[i][j] < 0) {
catalanNums[i][j] = catalan(i, j);
}
}
}
return catalan(n, 0);
}
private:
int catalan(int outside, int inside) {
if (catalanNums[outside][inside] >= 0) return catalanNums[outside][inside];
if (outside == 0) return 1; //stack full
if (inside == 0) return catalan(outside - 1, 1); //stack empty
return catalan(outside - 1, inside + 1) + catalan(outside, inside - 1);
}
vector<vector<int>> catalanNums;
};
解法5:还是DP。
但没用memorization会超时。
class Solution {
public:
/**
* @paramn n: An integer
* @return: A list of root
*/
int numTrees(int n) {
return helper(1, n);
}
private:
int helper(int begin, int end) {
if (begin > end) return 1; //NULL is one solution
int count = 0;
for (int i = begin; i <= end; ++i) {
int leftCount = helper(begin, i - 1); //leftTree
int rightCount = helper(i + 1, end); //rightTree
count += leftCount * rightCount;
}
return count;
}
};
解法6: 解法5的memorization版本。
注意:解法2的dp[k]即解法6的dp[i][j] (j - i + 1 == k),即解法6的二维矩阵很多都是冗余的,即(1…5)和(2…6)所构成的BST树的数目都是一样的,即(0…4)所构成的BST树的数目。
代码如下:
class Solution {
public:
/**
* @paramn n: An integer
* @return: A list of root
*/
int numTrees(int n) {
dp.resize(n + 1, vector<int>(n + 1, -1));
for (int i = 0; i <= n; ++i)
for (int j = 0; j <=n; ++j) {
if (i >= j) dp[i][j] = 1;
}
return helper(1, n);
}
private:
int helper(int begin, int end) {
if (begin >= end) return 1; //NULL is one solution
int count = 0, leftCount = 0, rightCount = 0;
for (int i = begin; i <= end; ++i) {
leftCount = dp[begin][i - 1] < 0 ? helper(begin, i - 1) : dp[begin][i - 1]; //leftTree
if (i < end) {
rightCount = dp[i + 1][end] < 0 ? helper(i + 1, end) : dp[i + 1][end]; //rightTree
} else {
rightCount = 1;
}
count += leftCount * rightCount;
}
dp[begin][end] = count;
return count;
}
vector<vector<int>> dp;
};
博客围绕给定n值,求解存储值为1…n的不同结构二叉搜索树(BST)数量展开。介绍了以i为根节点的BST树左右子树构成思路,给出了用Catalan数公式求解的方法,还列举了多种DP解法,包括不同的实现形式及递归版等。
1684

被折叠的 条评论
为什么被折叠?



