题目:
给你一个整数 n
,求恰由 n
个节点组成且节点值从 1
到 n
互不相同的 二叉搜索树 有多少种?返回满足题意的二叉搜索树的种数。
示例一:
输入:n = 3 输出:5
示例二:
输入:n = 1 输出:1
提示:
1 <= n <= 19
思路:
这道题使用了动态规划的dp数组来解决问题,想要先思考这道题的dp数组是什么含义。
这道题的dp数组含义为结点为i时能够构成二叉搜索树的个数。
然后我们可以想到结点个数分别为1和2时能够组成的二插搜索树的个数分别为1和2,之后我们就可以去想之后的dp数组应该如何去计算。
这里我们通过下面着段代码去计算后面的dp数组
dp[i] += dp[j - 1] * dp[i - j];
其中的i为结点个数,j为此时的根节点,因为这道题计算的时二叉搜索树,所以当我们确定以谁为根节点时,左右子树的个数都可以确定下来
左子树结点个数为(j - 1)
右子树结点个数为(i - j)
这样我们就能解决这道题目,但是在后面计算的过程中我们会发现两个特殊情况:
1.dp[0]该赋值为什么
因为有一些情况下确实会出现dp[0]去乘以一个数字来计算,所以我们将dp[0]赋值为1,这样就可以解决这个问题,
2.n = 1时的越界问题
若此时n = 1时,我们在赋值dp[2]时会发生越界问题,对于这种情况,我们有两种解决方法,第一种是单独写一条判断语句来解决,第二种就是将所以的下标向前移动一位,即结点个数为1时代表dp[0],结点个数为2时代表dp[1],这样也可以解决这个问题
代码:
class Solution {
public:
int numTrees(int n) {
//dp[i]表示结点为i时能够组成二插搜素树的个数
if(n == 1){//因为我们在循环前赋值时赋值到了dp[2],所以我们想要判断n=1的情况,不然会跃界
return 1;
}
vector<int> dp(n + 1);//范围开到n + 1的原因是为了包括结点个数为n时的情况
dp[0] = 1;//因为后面我们想要使用乘法来完成当前根结点组成二叉树的情况,所以我们想要将dp[0]赋值为1
dp[1] = 1;
dp[2] = 2;
for(int i = 3; i <= n; i++){
//确定根节点为谁,就能确定左子树结点个数(j - 1)与右子树结点个数(i - j)
for(int j = 1; j <= i; j++){//以j为根结点
dp[i] += dp[j - 1] * dp[i - j];
}
}
return dp[n];
}
};
时间复杂度:O()
n为结点个数
空间复杂度:O(n)
额外使用n+1的空间来存放数据,但是空间复杂度不是O(n + 1),而是O(n),