【js刷题日记】不同的二叉搜索树

99 阅读1分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第4天,点击查看活动详情

题目描述

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

输入: n = 3
输出: 5

示例 2:

输入: n = 1
输出: 1

提示:

  • 1 <= n <= 19

关于二叉搜索树

二叉搜索树是一个有序树,他有着几个特点

  • 如果左子树不为空,那么左子树上所有结点的值均小于根结点的值
  • 如果右子树不为空,那么右子树上所有结点的值均大于根结点的值
  • 左右子树也分别为二叉搜索树

动态规划

如果整数1 ~ n中的 k 作为根节点值,则 1 ~ k-1 会去构建左子树,k+1 ~ n 会去构建右子树

当n为1,有一棵搜索树;

n=2,有两棵搜索树;

n为3的时候,就有5棵搜索树,这时,就可以找规律了:

1为头结点和3为头结点时,子树的布局其实是和n=2时一样的,可以分别对应上,同时,2为头结点时,子树布局和n=1时是一样的,这样我们就找到重叠子问题了。可以通过dp[1]和dp[2]推导出dp[3]

dp[3]=元素1为头结点的搜索树数量+元素2为头结点的搜索树数量+元素3为头结点的搜索树数量

  • 元素1为头结点搜索树的数量 = 右子树有2个元素的搜索树数量 * 左子树有0个元素的搜索树数量
  • 元素2为头结点搜索树的数量 = 右子树有1个元素的搜索树数量 * 左子树有1个元素的搜索树数量
  • 元素3为头结点搜索树的数量 = 右子树有0个元素的搜索树数量 * 左子树有2个元素的搜索树数量

所以dp[3] = dp[2] * dp[0] + dp[1] * dp[1] + dp[0] * dp[2]

接下来我们就可以推出递推关系了dp[i] = dp[i] + dp[以j为头结点左子树节点数量] * dp[以j为头结点右子树节点数量]

所以递推公式就是dp[i] = dp[i] + dp[j - 1] * dp[i - j]

这里的j-1 是以j为头结点左子树节点数量,i-j 是以j为头结点右子树节点数量

那么就可以写出这道题的做法

var numTrees = function(n) {
    const dp = new Array(n + 1).fill(0);
    dp[0] = 1;
    dp[1] = 1;

    for (let i = 2; i <= n; ++i) {
        for (let j = 1; j <= i; ++j) {
            dp[i] += dp[j - 1] * dp[i - j];
        }
    }
    return dp[n];
};

js中的fill() 方法用于将一个固定值替换数组的元素。此处给数组中每个元素赋值0用来初始化数组

时间复杂度为O(n2)

空间复杂度为O(n)

动态规划算是比较常用的算法了,之前刷过好多题都有它的身影,像是计算岛屿数量、飞地数量等等,所以这一块一定要好好掌握。