动态规划-96.不同的⼆叉搜索树

47 阅读2分钟

4月日新计划更文活动 第6天

前言

动态规划专题,从简到难通关动态规划。

每日一题

今天的题目是 96. 不同的二叉搜索树,难度为中等

给你一个整数 n ,求恰由 n 个节点组成且节点值从 1 到 n 互不相同的 二叉搜索树 有多少种?返回满足题意的二叉搜索树的种数。

 

示例 1:

输入: n = 3
输出: 5

示例 2:

输入: n = 1
输出: 1

 

提示:

  • 1 <= n <= 19

题解

二叉搜索树

做这道题,首先需要了解一下什么是二叉搜索树:

二叉搜索树是一个有序树

  • 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
  • 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
  • 它的左、右子树也分别为二叉排序树

下面这两棵树都是搜索树

image.png

image.png

思路

image.png

n=3

我们可以发现,当将 1 作为头结点,剩下的两个作为子节点,分布在下面的 2,3 的节点排布是和 n=2 的情况完全相同的,相等的,当 3 为 头结点的时候也是这个样子

我们可以稍微列一下 n = 4 的情况对比一下上面说的是对的

image.png

当 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]
};

image.png