96.不同的二叉搜索树
给你一个整数 n
,求恰由 n
个节点组成且节点值从 1
到 n
互不相同的 二叉搜索树 有多少种?返回满足题意的二叉搜索树的种数。
示例 1:
输入: n = 3
输出: 5
示例 2:
输入: n = 1
输出: 1
提示:
- 1 ≤ n ≤ 19 1 \leq n \leq 19 1≤n≤19
解法一(动态规划)
思路分析:
- 对于求不同的二叉搜索树问题,可以发现,当
n = 4
时,二叉搜索树一共有4个节点,那么,这四个节点可以轮流为根节点,并在左右子树分别进行二叉搜索树。即左子树可能有0个,1个,2个,3个节点,而右子树也有0个,1个,2个,3个节点。 - 如此,对于
n = 4
时,求不同二叉搜索树的问题,进一步分化为,求3个节点的不同二叉搜索树,2个节点的不同二叉搜索树,1个节点的不同二叉搜索树,0个节点的不同二叉搜索树。 - 根据上面的分析,问题分解为多个子问题,考虑使用动态规划算法;即动规五步曲:
- 定义状态:即
dp[i]
表示,由i
个节点可以组成dp[i]
个不同的二叉搜索树 - 确定状态转移方程:即
dp[i] = dp[0] * dp[i-1] + dp[1] * dp[i-2] + ... + dp[i-1] * dp[0]
;进一步可以表示为:dp[i] = dp[j] * dp[i-j-1]
,其中j
在区间[0, i)
中 - 确定初始状态:即
dp[0] = 1; dp[1] = 1
,因为由0或1个节点都只可以组成1个不同的二叉搜索树 - 确定
dp
遍历顺序:即从i = 2
开始往后遍历,因为dp[0]
和dp[1]
已经确定;同时得到dp[i]
的状态需要依赖i
之前的状态 - 打印
dp
数组,检验是否符合思路和题意
- 定义状态:即
实现代码如下:
class Solution {
public int numTrees(int n) {
// dp[i]的含义:由i个连续整数节点可以组成dp[i]种不同的二叉搜索树
int[] dp = new int[n+1]; // 状态转移方程为:dp[i] += dp[j] * dp[i-j-1]
// 初始化dp
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];
}
}
提交结果如下:
解答成功:
执行耗时:0 ms,击败了100.00% 的Java用户
内存消耗:39.2 MB,击败了91.72% 的Java用户
复杂度分析:
- 时间复杂度: O ( n 2 ) O(n^2) O(n2)
- 空间复杂度: O ( n ) O(n) O(n)