「这是我参与11月更文挑战的第16天,活动详情查看:2021最后一次更文挑战」
前言
力扣第九十六题 不同的二叉搜索树
如下所示:
给你一个整数 n
,求恰由 n
个节点组成且节点值从 1
到 n
互不相同的 二叉搜索树 有多少种?返回满足题意的二叉搜索树的种数。
示例 1:
输入: n = 3
输出: 5
复制代码
一、思路
这一题比上一题不同的是:只关心二叉搜素树的种树,而不关系树的节点了。
所以一开始我的思路也是用递归来做的,去除了保留路径的部分,仅得到左孩子的个数和右孩子的个数即可。7代码如下所示:
public int dfs(int start, int end) {
int ret = 0;
if (start > end) {
return 1;
}
// 枚举可行根节点
for (int i = start; i <= end; i++) {
// 找到所有的左子树
int left = dfs(start, i - 1);
// 找到所有的右子树
int right = dfs(i + 1, end);
ret += left * right;
}
return ret;
}
复制代码
但是很可惜,在 n=18
时会提示超出时间限制!
那既然不能用 递归
来自顶向下,那就只能使用 动态规划
来实现 自底向上
了。
首先我们先看一下 递归
中调用的地方,我们发现在递归中一直在求 1 ~ i-1
和 i+1 ~ n
左孩子和右孩子个数。
我们不妨设 dp[i]
为长度为 i
的不同二叉搜索树的个数。
可以有 dp[n] = dp[0]dp[n-1] + dp[1]dp[n-2] + dp[2]dp[n-3] + ... + dp[n-1]dp[0]
公式解释:
因为dp[n]
就是选择1 ~ n
中的某一个元素i
为根,再将1 ~ i-1
中的元素作为左孩子,i+1 ~ n
中的元素作为右孩子。
dp[0]dp[n-1]
就表示选择了第一个元素作为根有多少种结果。
此外,因为二叉搜索树的特殊性。对于1, 2, 3
和1, 2, 4
组成的二叉搜索树的种数是相同的。所以我们在公式中可以忽略元素的值,而只关注元素的个数,因为相同元素个数组成的二叉搜索树的种树是相同的。
二、实现
实现代码
实现代码与思路中保持一致
public int numTrees(int n) {
int[] dp = new int[n +1];
dp[0] = dp[1] = 1; // 0个和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];
}
复制代码
测试代码
public static void main(String[] args) {
int ret = new Number96().numTrees(3);
System.out.println(ret);
}
复制代码
结果
三、总结
感谢看到最后,非常荣幸能够帮助到你~♥
如果你觉得我写的还不错的话,不妨给我点个赞吧!如有疑问,也可评论区见~