96. 不同的二叉搜索树
文章目录
- [96. 不同的二叉搜索树](https://leetcode.cn/problems/unique-binary-search-trees/)
- 一、题目
- 二、题解
一、题目
给你一个整数 n
,求恰由 n
个节点组成且节点值从 1
到 n
互不相同的 二叉搜索树 有多少种?返回满足题意的二叉搜索树的种数。
示例 1:
输入:n = 3
输出:5
示例 2:
输入:n = 1
输出:1
提示:
1 <= n <= 19
二、题解
这道题目是求由 n 个节点组成的且节点值从 1 到 n 互不相同的二叉搜索树的种数,我们可以通过动态规划来解决。
算法思路:
- 分析问题特点:首先,我们需要明确什么是二叉搜索树(BST)以及如何构建它们。BST 是一种特殊的二叉树,满足以下性质:左子树上的所有节点的值都小于根节点的值,右子树上的所有节点的值都大于根节点的值,且左右子树也分别是 BST。
- 问题分解:考虑解决问题的分解方式。假设我们已经知道了 1 到 i-1 个节点可以构成的 BST 种数,以及 i+1 到 n 个节点可以构成的 BST 种数,那么如何计算由 i 个节点构成的 BST 种数呢?
- 以节点 i 为根节点:我们可以考虑将节点 i 作为根节点,那么就需要分别计算左子树和右子树的种数。左子树有 i-1 个节点,右子树有 n-i 个节点。(比如i = 3(头结点数字是3,结合题干给的图),n = 3, 那么左子树有i-1 = 2个节点,右子树则为0个节点;i= 2,左子树有1个节点,右子树有一个节点;i = 1,左子树有0个节点,右子树有两个节点)
- 计算左子树和右子树的种数:根据分步思考,左子树和右子树的种数可以通过之前计算得到,即 dp[i-1] 和 dp[n-i]。
- 累加可能性:将左子树和右子树的种数相乘,然后将所有可能的左右子树组合累加起来,即
dp[i] += dp[j-1] * dp[i-j]
,其中 j 表示根节点的值,从 1 到 i 遍历。 - 递推更新数组 dp:从节点数为 1 开始,逐步递推计算 dp 数组,直到节点数为 n,得到最终结果。
具体实现:
class Solution {
public:
int numTrees(int n) {
vector<int> dp;
dp.push_back(1); // 初始情况:空树的种数为 1
for(int i = 1; i <= n; i++){
dp.push_back(0); // 初始化当前节点数的种数为 0
for(int j = 1; j <= i; j++){
dp[i] += dp[j-1] * dp[i-j]; // 累加可能性
}
}
return dp[n];
}
};
算法分析:
- 时间复杂度:外层循环遍历节点数从 1 到 n,内层循环遍历节点数 j 从 1 到 i,因此总的时间复杂度为 O(n^2)。
- 空间复杂度:使用了一个大小为 n+1 的数组来存储 dp 值,因此空间复杂度为 O(n)。
- 动态规划优势:动态规划的优势在于避免了重复计算,通过保存中间结果,将问题分解为子问题,提高了计算效率。