动态规划 -- 不同的二叉搜索树

动态规划 – 不同的二叉搜索树

什么是动态规划?

将一个问题拆分成一个个子问题,直到子问题可以直接解决。然后将子问题答案保存以减少重复计算,并通过子问题答案反推,得出原问题解的一种方式。

核心思想:拆分子问题,记住过往,减少重复计算

解题思路:

  • 穷举分析
  • 确定边界
  • 找出规律,确定最优子结构
  • 写出状态转移方程

什么是二叉搜索树?

二叉搜索树BST(Binary Search/ Sort Tree),也称为二叉查找树,二叉排序树,顾名思义是二叉树的特种树,专注于检索,特点是让节点按照数据的一定的规律摆放,从而让搜索某个节点特别的高效。

特点:

  • 左子树上所有结点的值均小于等于它的根节点值
  • 右子树上所有结点的值均大于等于它的根节点值

利用动态规划解决问题

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

示例 1:

输入:n = 3
输出:5

示例 2:

输入:n = 1
输出:1
  1. 穷举分析

对于1-n的数,我们遍历每一个数字,将当前数字定为i;

当i = 1时,生成的二叉搜索树为1

当i = 2时,1作为根节点时,2作为它的右节点;2作为根节点时,1作为它的左节点

当i = 3时:排列出5种不同的二叉搜索树

  • 1作为根节点时,2、3作为右节点,同时对于2、3参照i=2时可以排列出两种符合二叉搜索树的节点;
  • 2作为根节点时,只能是1作为左节点,3作为右节点
  • 3作为根节点时,1、2作为右节点,参照i = 2 时进行排列,可以得出两种结果。

当 i 作为根节点时,将1… (i-1)作为左节点,(i+1) … n作为右节点;

  1. 确定边界

定义G(n)函数:长度为n的序列能构成的不同二叉搜索树的个数

定义F(i,n):当i为根、序列长为n时能构成的不同二叉搜索树的个数

从穷举分析中得出结论:G(n)=i=∑F(i,n)

边界情况:序列长度为1和序列长度为0 时,G(0) = 1;G(1) = 1

  1. 找出规律,确定最优子结构

给定序列 1…n,我们选择数字 i 作为根,则根为 i 的所有二叉搜索树的集合是左子树集合和右子树集合的笛卡尔积

例如:不同二叉搜索树的个数为F(4,9)。其中[1,2,3]作为左节点,[5,6,7,8,9]作为右节点。

  • [1,2,3]构建的左子树个数为G(3);[5,6,7,8,9]构建的右子树个数为G(5)
  • 因此 F(4,9) = G(3) * G(5)

从上述分析中得出,G(n)的结果只和序列长度有关 因此推出公式 *F(i,n) = G(i-1)G(n-i)

  1. 写出状态转移方程

G(n)=i=∑F(i,n) 与 **F(i,n) = G(i-1)*G(n-i)**相结合,推出状态转移方程:*G(n) = ∑ G(i-1)G(n-i) ( i 属于 1-n)

根据推论出的状态转移方程解题:

/**
 * @param {number} n
 * @return {number}
 */
var numTrees = function(n) {
    //定义一个长度为n+1的数组
    const G = new Array(n+1).fill(0);

    //定义边界条件
    G[0] = 1;
    G[1] = 1;
    
    //实现公式条件
    //G(2) = G(0)*G(1) + G(1)*G(0) = 2
    //G(3) = G(0)*G(2) + G(1)*G(1) + G(2)*G(0) = 5
    for(let i = 2; i <= n; i++){
        for(let j = 1; j <= i; j++){
            G[i] += G[j-1] * G[i-j];
        }
    }
    return G[n];
};
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值