DailyChallenge
96. 不同的二叉搜索树
20200715
难度:中等
题目描述
给定一个整数 n,求以 1 ... n 为节点组成的二叉搜索树有多少种?
示例:
输入: 3
输出: 5
解释:
给定 n = 3, 一共有 5 种不同结构的二叉搜索树:
1 3 3 2 1
\ / / / \ \
3 2 1 1 3 2
/ / \ \
2 1 2 3
链接:<https://leetcode-cn.com/problems/unique-binary-search-tree
Solution
- 方法一:动态规划
思路:
给定一个有序序列 ,为了构建出一棵二叉搜索树,我们可以遍历每个数字
,将该数字作为树根,将
序列作为左子树,将
序列作为右子树。接着我们可以按照同样的方式递归构建左子树和右子树。
在上述构建的过程中,由于根的值不同,因此我们能保证每棵二叉搜索树是唯一的。
由此可见,原问题可以分解成规模较小的两个子问题,且子问题的解可以复用。因此,我们可以想到使用动态规划来求解本题。
算法
题目要求是计算不同二叉搜索树的个数。为此,我们可以定义两个函数:
: 长度为 n 的序列能构成的不同二叉搜索树的个数。
: 以 i 为根、序列长度为 n 的不同二叉搜索树个数
。
可见, 是我们求解需要的函数。
稍后我们将看到, 可以从
得到,而
又会递归地依赖于
。
首先,根据上一节中的思路,不同的二叉搜索树的总数 ,是对遍历所有
的
之和。换言之:
对于边界情况,当序列长度为 11(只有根)或为 00(空树)时,只有一种情况,即:
不同二叉搜索树的个数为 。我们将
构建不同左子树的数量表示为
, 从
构建不同右子树的数量表示为
,注意到
和序列的内容无关,只和序列的长度有关。于是,
。 因此,我们可以得到以下公式:
可得:
class Solution {
public int numTrees(int n) {
int[] dp = new int[n + 1];
dp[0] = 1;
dp[1] = 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];
}
}
- 方法二:数学
事实上我们在方法一中推导出的 G(n)函数的值在数学上被称为卡塔兰数 。卡塔兰数更便于计算的定义如下:
class Solution {
public int numTrees(int n) {
// 提示:我们在这里需要用 long 类型防止计算过程中的溢出
long dp = 1;
for (int i = 0; i < n; ++i) {
dp = dp * 2 * (2 * i + 1) / (i + 2);
}
return (int) dp;
}
}
链接:leetcode-cn.com/problems/un…
❝我的公众号:GitKid
暂时每日分享LeetCode,我在不断学习的过程中,公众号也在不断充实,欢迎大家扫码关注。
TODO:动态规划专题
本文使用 mdnice 排版