这是一道mid的动态规划题,据传是华为OD面试最喜欢手撕的题之一,我们先来看题目
给你一个整数 n
,求恰由 n
个节点组成且节点值从 1
到 n
互不相同的 二叉搜索树 有多少种?返回满足题意的二叉搜索树的种数。
在做这个题之前我们先来了解一下二叉搜索树的定义
二叉搜索树是一种特殊的二叉树结构,它满足以下性质:
- 每个节点都有一个键值,且节点的键值是唯一的。
- 左子树中的所有节点的键值小于根节点的键值。
- 右子树中的所有节点的键值大于根节点的键值。
- 左子树和右子树也是二叉搜索树。
这种特性使得二叉搜索树可以高效地进行搜索、插入和删除操作。
简而言之,越往左越小,越往右越大。
了解完定义后,我们就可以来看这道题目了,一开始看并没有什么思路,那就先来分析一下样例,输入为3输出为5,我们来把这5中情况表示一下
画的比较抽线大家凑合看,可以看到以1为节点的有两种,但都是左侧为0个右侧为2个,以2为根节点的有一种,以三为根节点的有两种,都是右侧为2个。这些结论还不足以支持我们做题,那我们就再画一下n=1和n=2的情况
可以看出n=1只有一种情况,n=2有两种情况。那我们来总结一下规律当n=1时,sum直接等于1,当n=2时,sum= 2,这时我们是不是可以延申为以任意一个大于0小于等于2的数当作根节点,下面以另外可取的数值形成另一个二叉搜索树,可能有点抽象,那我们再来看n=3,是不是可以视为根节点为1,下面有以2和3组成的二叉搜索树,实际上也就是当n=2时候的总数,再加上根节点等于2,由于我们前面说过,根节点为2只能是左右各一个,所以只能有一种,再加上根节点为3,和根节点为1同理,有两种情况,这样我们就可以得出结果5来。
再看一下规律,以1为节点的时候得出的数是不是左边可以成立的情况*右边可以成立的情况,也就是1*2,以2为节点的时候得出的数是不是1*1,而3为节点则是2*1,这样我们可以基本的出规律dp[i]+=dp[j-1]*dp[i-j],dp数组的含义有多少种不同的二叉搜索树,为什么是j-1和i-j呢,我们可以想一下,我们要算n=i,当我们以一个小于i的数j为根节点的时候,他的左边是不是有j-1种选择,就像j=3,左面是不是只能是3-1=2种选择,右边同理也就只能是i-j种选择。所以我们可以确定我们的递推公式
for(int i=1;i<=n;i++){
for(int j=1;j<=i;j++){
dp[i]=dp[i]+dp[j-1]*dp[i-j];
}
}
递推公式确定后,我们来看一下初始化,显然dp[0]不能初始化为0,因为如果dp[0]=0,那么后面的所有数都为0,所以dp[0]=1,后面的就不需要初始化了,dp[1]可以通过dp[1-1]*dp[2-1]来推的。遍历顺序和打印就非常简单了,我们在这里就不做过多讲解了,下面贴一下AC代码
class Solution {
public:
int numTrees(int n) {
vector<int> dp(n + 1);
dp[0] = 1;
for(int i=1;i<=n;i++){
for(int j=1;j<=i;j++){
dp[i]=dp[i]+dp[j-1]*dp[i-j];
}
}
return dp[n];
}
};
有什么不懂的地方或者我有什么错误的地方欢迎各位大佬在评论区和我交流,谢谢各位,后续我会持续更新力扣(基本上都是剑指offer和top100的题)和c++高性能服务器,感兴趣的朋友可以关注一下。