前言
动态规划专题,从简到难通关动态规划。
每日一题
今天的题目是 96. 不同的二叉搜索树,难度为中等
给你一个整数 n
,求恰由 n
个节点组成且节点值从 1
到 n
互不相同的 二叉搜索树 有多少种?返回满足题意的二叉搜索树的种数。
示例 1:
输入: n = 3
输出: 5
示例 2:
输入: n = 1
输出: 1
提示:
1 <= n <= 19
题解
二叉搜索树
做这道题,首先需要了解一下什么是二叉搜索树:
二叉搜索树是一个有序树
- 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
- 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
- 它的左、右子树也分别为二叉排序树
下面这两棵树都是搜索树
思路
n=3
我们可以发现,当将 1 作为头结点,剩下的两个作为子节点,分布在下面的 2,3 的节点排布是和 n=2 的情况完全相同的,相等的,当 3 为 头结点的时候也是这个样子
我们可以稍微列一下 n = 4 的情况对比一下上面说的是对的
当 2 为头结点的时候,子节点的排布是和 n =1 完全相同的
这样我们就能够推测出递推公式
dp[i] += dp[以j为头结点左⼦树节点数量] * dp[以j为头结点右⼦树节点数量]
然后根据递推五部曲,继续确定dp数组
dp[i] : 1到i为节点组成的⼆叉搜索树的个数为dp[i]。
dp数组初始化 dp[0] = 1, dp[1] = 1, dp[2] = 2
确定遍历顺序
⾸先⼀定是遍历节点数,从递归公式:
dp[i] += dp[以j为头结点左⼦树节点数量] * dp[以j为头结点右⼦树节点数量]
可以看出,节点数为i的状态是依靠 i之前节点数的状态。
那么遍历i⾥⾯每⼀个数作为头结点的状态,⽤j来遍历
for (let i = 1; i <= n; i++) {
for (let j = 1; j <= i; j++) {
dp[i] += dp[j - 1] * dp[i - j];
}
}
n为5时候的dp数组状态
| 0 | 1 | 2 | 3 | 4 | 5 | | 1 | 1 | 2 | 5 | 14 | 42 |
最后得到代码:
function numTrees(n: number): number {
let dp = new Array(n+1).fill(0)
dp[0] = 1
dp[1] = 1
dp[2] = 2
for (let i = 3; i <= n; i++) {
for (let j = 1; j <= i; j++) {
dp[i] += dp[j - 1] * dp[i - j];
}
}
return dp[n]
};