不说废话,直戳盲点,打通思路, 举一反三。
题目描述
给定一个整数 n,求以 1 … n 为节点组成的二叉搜索树有多少种?
输入: 3
输出: 5
解释:
给定 n = 3, 一共有 5 种不同结构的二叉搜索树:
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/unique-binary-search-trees
先上代码
执行用时 :4 ms, 在所有 C++ 提交中击败了89.08%的用户
内存消耗 :8.2 MB, 在所有 C++ 提交中击败了57.19%的用户
class Solution {
public:
int numTrees(int n) {
if(n == 0)
return 0;
vector<int> dp(n+1);
dp[0] = 1;
dp[1] = 1;
for(int i=2;i<=n;++i){
for(int index = 1; index <=i; ++index){
dp[i] += dp[index-1] * dp[i-index];
}
}
return dp[n];
}
};
算法思路
是一个动态规划问题, 使用诸如递归等方式会导致超时。
- 设 d p ( n ) dp(n) dp(n)表示输入为n时,有多少种不同的二叉搜索树。易知: d p ( 1 ) = 1 dp(1)=1 dp(1)=1。
- 设 f n ( i ) f_n(i) fn(i)表示当输入为n时,以 i 作为根,有多少种不同的二叉搜索树。
- 那么,我们很容易得到下面的等式: (1) d p ( n ) = f n ( 1 ) + f n ( 2 ) + . . . + f n ( n ) dp(n) = f_n(1) + f_n(2)+...+f_n(n)\tag{1} dp(n)=fn(1)+fn(2)+...+fn(n)(1)
- 而当输入为n,以 i 作为根时,不同的二叉搜索树的种类数量等于左子树的种类数目乘右子树的种类数目,即: (2) f n ( i ) = d p ( i − 1 ) ∗ d p ( n − i ) f_n(i) = dp(i-1)*dp(n-i)\tag{2} fn(i)=dp(i−1)∗dp(n−i)(2) 这里,由于生成二叉搜索树的种类只与有序数列中数字的种类数目有关,而与数字的大小无关,所以 i+1, i+2,…,n 这组数生成的二叉搜索树的种类数目与 1,2,…,n-i 生成的二叉搜索树种类数目相同,所以后半部分直接用 d p ( n − i ) dp(n-i) dp(n−i)表示。
- 由(1)式和(2)式,我们可以得到下面的结论: d p ( n ) = d p ( 0 ) ∗ d p ( n − 1 ) + d p ( 1 ) ∗ d p ( n − 2 ) + . . . + d p ( n − 1 ) ∗ d p ( 0 ) dp(n) = dp(0)*dp(n-1) + dp(1)*dp(n-2)+...+dp(n-1)*dp(0) dp(n)=dp(0)∗dp(n−1)+dp(1)∗dp(n−2)+...+dp(n−1)∗dp(0)即 (3) d p ( n ) = ∑ i = 1 n d p ( i − 1 ) ∗ d p ( n − i ) dp(n) = \sum_{i=1}^{n}dp(i-1)*dp(n-i)\tag{3} dp(n)=i=1∑ndp(i−1)∗dp(n−i)(3)
- 有了(3)式作为理论基础,我们可以使用动态规划的方式去解决上面的问题。而且,我们必须将 d p ( 0 ) dp(0) dp(0)设置为1(想想为什么?).
学习和思考
- 在这个问题中,很难一下子得到一个动态规划的状态转移方程。要学会通过问题分解,分别解决而后整合公式的方式,建模一个动态规划问题并且解决。
- 如果需要得到不同种类的二叉搜索树具体是什么,该怎么办? 参考不同的不同的二叉搜索树II。