【动态规划 NK刷题记 DP5 之 有多少个不同的二叉搜索树

这篇具有很好参考价值的文章主要介绍了【动态规划 NK刷题记 DP5 之 有多少个不同的二叉搜索树。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

 一、题解部分

1.1题目

1.2铺垫

1.3.题解:

二、法一:递归实现

1.输入数据,创建动态数组

 2.断言dp指针,并给它赋值

3.打印结果并调用函数

3.1注意:

4.实现函数binarytree

4.1先将动态数组dp[i]中特殊的值给出来,比如i=1,i=0时

4.2然后循环遍历节点的数量为i时,根节点j的不同取值情况,并递归求出不同的dp[i]

4.3返回dp[n]的值

4.4函数binarytree的完整代码 

 5.完整C语言代码

三、法二:递推求解(从前往后)

1.循环实现 

2.方法二完整C语言代码 

四、总结


 一、题解部分

1.1题目

这里我们将题目给复制过来,方便大家浏览

也可以点击下面的友情链接,进入牛客网看题目点击此处跳转

DP5 有多少个不同的二叉搜索树

描述

给定一个由节点值从 1 到 n 的 n 个节点。请问有多少种不同的方法用这 n 个节点构成互不相同的二叉搜索树。

数据范围:1≤n≤19 

输入描述:

仅一行输入一个正整数 n ,表示节点的数量。

输出描述:

输出组成不同二叉搜索树的方法树

1.2铺垫

在写这道题之前,我们首先要理解这样一个问题,即什么是二叉树?这里我们附上相关链接点这里直接跳转-->二叉树的概念,在了解完二叉树的概念之后,二叉树有着这样一个性质即:1.每棵子树头节点的值都比各自左子树上所有节点值要大,也都比各自右子树上所有节点值要小2.根节点值可以从1到n可以任意取值,这里我们给出了节点值从1到6的二叉搜索树的其中两种情况,希望可以帮助你更好的理解二叉树的两个性质

                【动态规划 NK刷题记 DP5 之 有多少个不同的二叉搜索树【动态规划 NK刷题记 DP5 之 有多少个不同的二叉搜索树

1.3.题解:

        1.定义dp[i]表示节点数目为i时组成不同二叉树的方法数,注意节点元素值是从1开始的
        2.由于二叉树左子树比根节点小,右子树比根节点要大,所以根节点不同也会出现不同种类的二叉搜索树,我们用j来表示当前根节点值,其中j的取值范围为(0<j<=n)
        3.我们现在可以知道一个任意的节点数为i的二叉搜索树有j种方式,这些方式相互独立所以是相加,这里涉及高中数学的排列组合相加与相乘原理,我们给出相关链接:                                         点击直接跳转 --> 排列组合之相加相乘原理的区别
        4.单看其中任意一种方式假设此时根节点的节点值已经确定就是j,节点的数量为i(0<i<=n)想得到根节点值为j时(0<j<=n)且节点数量为i的其中一个任意的二叉搜索树,需要分成两个步骤
    1.排好j的左子树    2.排好j的右子树
想得到根节点值为j时排这个节点数目为i的二叉树的方法数,只需要把这两个子二叉树的方法数相乘就可
        5.左子树:节点元素为:1~(j-1)j-1个节点 排这个子二叉树方法数为dp[j-1]
        6.右子树:节点元素为:(j+1)~i  i-j个节点 排这个子二叉树方法数为dp[i-j]
         这个地方要注意 二叉树的方法数与节点元素的具体值无关,只与节点的数量有关
        7.表达式:dp[i] = sum(dp[j-1] + dp[i-j]),sum指把所有的不同的根节点情况的方法数相加的结果

二、法一:递归实现

1.输入数据,创建动态数组

   int n = 0;
	scanf("%d", &n);
	int* dp = (int*)malloc((n + 1) * sizeof(int));//申请一个动态数组dp[i]来表示节点数为i时表示的不同二叉树搜索树的种数

 2.断言dp指针,并给它赋值

    assert(dp);//断言,确保已经申请成功,防止出现空指针的情况
	memset(dp, 0, (n + 1) * sizeof(int));//初始化数组dp为0

3.打印结果并调用函数

printf("%d\n", binarytree(n, dp));

3.1注意:

为了使我们main函数部分变得更简洁,我们将创建一个叫binarytree的函数,它的功能也很单一,即求节点的数量为n时,其表示的不同二叉搜索树的方法数,并返回给我们的main函数

4.实现函数binarytree

4.1先将动态数组dp[i]中特殊的值给出来,比如i=1,i=0时

p[0] = 1;//只有左子树或者右子树的情况,自然另一边的情况唯一,方法数为1。
p[1] = 1;

      注意,i == 1时很好理解,此时节点的数量为1,自然能表示的二叉搜索树的种类就只有一种,关键在于这个i == 0的情况是该如何理解,想一想,假设此时我想求一个节点数为i,根节点为j所能表示的二叉搜索树的种类,按照我们之前在题解中的观点,我想得到其中一个二叉搜索树,要分为两步,先排左子树,再排右子树,再将这两个子树相应节点数组成的二叉搜索树的方法树相乘即可,但现在如果根节点值j取到一个特殊值,只有比它大的节点或者比它小的节点了,那该怎么办呢?很明显,这如果是一道数学题,你应该马上会回答出来,节点数量不为0的那一段子树所组成的二叉搜索树的种树就是我们节点数为i,根节值点为j所能表示的二叉搜索树的方法数了!!!那是不是就等价于dp[0] = 1了呢,很显然,是这样的。

4.2然后循环遍历节点的数量为i时,根节点j的不同取值情况,并递归求出不同的dp[i]

for (int j = 1; j <= n; j++)//循环遍历所有不同根节点的情况
	{
 /*1.谁不知道就去递归求谁然后用动态数组保存起来,
            避免重复的去求*/
 /*2.这里要注意的一点是,节点元素i~i+3 组成二叉树的方法数目与节点元素1~4组成二叉树的方法种树相同*/
 /*3.                    节点元素为i~j d节点数目为(i-j+1)*/
		if (p[j - 1] > 0 && p[n - j] > 0)
			p[n] += p[j - 1] * p[n - j];
		else if (p[j - 1] > 0)
			p[n] += p[j - 1] * binarytree(n - j, p);
		else if (p[n - j] > 0)
			p[n] += binarytree(j - 1, p) * p[n - j];
		else p[n] += binarytree(j - 1, p) * binarytree(n - j);
	}

注意由于我们已经初始化数组dp为0,那么就可以通过dp[i]是否等于0来知道其是否已知,如果未知就通过递归,从后往前,第一次大的递归一直递归到n == 1 或者n == 2就会返回值(因为我们递归开始之前就给p[0]和p[1]赋了值),之后的递归会越来越快,因为已知数变得越来越多,从而一步一步算出我们想要的那个值

4.3返回dp[n]的值

	return p[n];//返回需要的值

4.4函数binarytree的完整代码 

int binarytree(int n, int* p)
{
	p[0] = 1;//只有左子树或者右子树的情况,自然另一边的情况唯一,方法数为1。
	p[1] = 1;
    if(n > 1)
 {
	for (int j = 1; j <= n; j++)//循环遍历所有不同根节点的情况
	{
 /*1.谁不知道就去递归求谁然后用动态数组保存起来,
            避免重复的去求*/
 /*2.这里要注意的一点是,节点元素i~i+3 组成二叉树的方法数目与节点元素1~4组成二叉树的方法种树相同*/
 /*3.                    节点元素为i~j d节点数目为(i-j+1)*/
		if (p[j - 1] > 0 && p[n - j] > 0)
			p[n] += p[j - 1] * p[n - j];
		else if (p[j - 1] > 0)
			p[n] += p[j - 1] * binarytree(n - j, p);
		else if (p[n - j] > 0)
			p[n] += binarytree(j - 1, p) * p[n - j];
		else p[n] += binarytree(j - 1, p) * binarytree(n - j);
	}
 }
	return p[n];//返回需要的值
}

 注意n==1要单独考虑,因为我们已经在递归的开始就给dp[1]赋上了值,不需要进入循环再算一遍

 5.完整C语言代码

//法一:动态规划之递归
#include <stdio.h>
#include<stdlib.h>
#include<string.h>
#include<assert.h>
int binarytree(int n, int* p)
{
	p[0] = 1;//只有左子树或者右子树的情况,自然另一边的情况唯一,方法数为1。
	p[1] = 1;
    if(n > 1)
  {
   for (int j = 1; j <= n; j++)//循环遍历所有不同根节点的情况
	{
      /*1.谁不知道就去递归求谁然后用动态数组保存起来,
            避免重复的去求*/
     /*2.这里要注意的一点是,节点元素i~i+3 组成二叉树的方法数目与节点元素1~4组成二叉树的方法种树 
        相同*/
       /*3.                    节点元素为i~j d节点数目为(i-j+1)*/
		if (p[j - 1] > 0 && p[n - j] > 0)
			p[n] += p[j - 1] * p[n - j];
		else if (p[j - 1] > 0)
			p[n] += p[j - 1] * binarytree(n - j, p);
		else if (p[n - j] > 0)
			p[n] += binarytree(j - 1, p) * p[n - j];
		else p[n] += binarytree(j - 1, p) * binarytree(n - j);
	}
}
	
	return p[n];//返回需要的值
}
int main()
{
	int n = 0;
	scanf("%d", &n);
	int* dp = (int*)malloc((n + 1) * sizeof(int));//申请一个动态数组dp[i]来表示节点数为i时的能表示的不同二叉树的种数
	assert(dp);//断言,确保已经申请成功
	memset(dp, 0, (n + 1) * sizeof(int));//初始化数组dp为0
	printf("%d\n", binarytree(n, dp));
	return 0;
}

三、法二:递推求解(从前往后)

注意这个方法二与方法一只有算法实现部分有所区别,所以前面的雷同部分我们直接省略

1.循环实现 

从前往后递推求dp[n],只需注意i从2开始计算即可,因为i=1和i=0已经已知了。

for (int i = 2; i <= n; i++)//循环递推一步一步得出dp[n],注意n == 1要分开讨论,防止出错
	for (int j = 1; j <= i; j++)//1~i谁都可以成为根节点,每一种情况都不同,需要都加起来
	{
		dp[i] += dp[j - 1] * dp[i - j];
	}//每一次第二个循环结束都可以算出一个值,即节点数为i(i > 1)组成的不同二叉树的方法树

我们直接给出完整代码

2.方法二完整C语言代码 

//法二:循环解法(递推)
#include <stdio.h>
#include<stdlib.h>
#include<string.h>
#include<assert.h>
int main() {
	int n = 0;
	scanf("%d", &n);
	int* dp = (int*)malloc((n + 1) * sizeof(int));//申请动态数组存储节点数为i时能组成的不同二叉树的方法数
	assert(dp);//确保dp已经申请成功
	memset(dp, 0, (n + 1) * sizeof(int));初始化dp的值为0
	p[0] = 1;//只有左子树或者右子树的情况,自然另一边的情况唯一,方法数为1。
	p[1] = 1;
	for (int i = 2; i <= n; i++)//循环递推一步一步得出dp[n],注意n == 1要分开讨论,防止出错
	for (int j = 1; j <= i; j++)//1~i谁都可以成为根节点,每一种情况都不同,需要都加起来
	{
		dp[i] += dp[j - 1] * dp[i - j];
	}//每一次第二个循环结束都可以算出一个值,即节点数为i(i > 1)组成的不同二叉树的方法树
	printf("%d", dp[n]);
	return 0;
}

四、总结

抛开时间复杂度和空间复杂度不谈,方法二的代码复杂度更得我青睐,这个题目与跳台阶和斐波拉契数列问题的不同之处是点击此处跳转斐波拉契(这里我们给出友情链接,有兴趣的朋友可以看一下),递推求解的时候每一次dp不再简单的只和最近的几个值有关,而是由于根节点j的不同,每一个值都不只用到一次,所以要把他们都用动态数组存起来,便于我们的使用,额外空间o(n)不能节省,最后谢谢大家的观看,欢迎评论区与我互动交流,共同进步!!!文章来源地址https://www.toymoban.com/news/detail-471960.html

到了这里,关于【动态规划 NK刷题记 DP5 之 有多少个不同的二叉搜索树的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • LeetCode算法题解(动态规划)|LeetCode343. 整数拆分、LeetCode96. 不同的二叉搜索树

    题目链接:343. 整数拆分 题目描述: 给定一个正整数  n  ,将其拆分为  k  个  正整数  的和(  k = 2  ),并使这些整数的乘积最大化。 返回  你可以获得的最大乘积  。 示例 1: 示例 2: 提示: 2 = n = 58 算法分析: 定义dp数组及下标含义: dp[i]表述正整数i拆分成k个正整数

    2024年02月04日
    浏览(40)
  • 【二十】【动态规划】879. 盈利计划、377. 组合总和 Ⅳ、96. 不同的二叉搜索树 ,三道题目深度解析

    动态规划就像是解决问题的一种策略,它可以帮助我们更高效地找到问题的解决方案。这个策略的核心思想就是将问题分解为一系列的小问题,并将每个小问题的解保存起来。这样,当我们需要解决原始问题的时候,我们就可以直接利用已经计算好的小问题的解,而不需要重

    2024年01月16日
    浏览(38)
  • 算法训练day41|动态规划 part03(LeetCode343. 整数拆分、96.不同的二叉搜索树)

    题目链接🔥🔥 给定一个正整数 n,将其拆分为至少两个正整数的和,并使这些整数的乘积最大化。 返回你可以获得的最大乘积。 示例 1: 输入: 2 输出: 1 解释: 2 = 1 + 1, 1 × 1 = 1。 示例 2: 输入: 10 输出: 36 解释: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36。 说明: 你可以假设 n 不小于 2 且不大于

    2024年02月10日
    浏览(39)
  • 【LeetCode题目详解】第九章 动态规划part03 343. 整数拆分 96.不同的二叉搜索树 (day41补)

    给定一个正整数  n  ,将其拆分为 k 个 正整数 的和(  k = 2  ),并使这些整数的乘积最大化。 返回 你可以获得的最大乘积  。 示例 1: 示例 2: 提示: 2 = n = 58 看到这道题目,都会想拆成两个呢,还是三个呢,还是四个.... 我们来看一下如何使用动规来解决。 # 动态规划 动

    2024年02月10日
    浏览(43)
  • 【动态规划】NK刷题之DP7 连续子数组的最大乘积

    ❤️博客主页: 小镇敲码人 🍏 欢迎关注:👍点赞 👂🏽留言 😍收藏 🌞在一切变好之前,我们总要经历一些不开心的日子,这段日子也许很长,也许只是一觉醒来。有时候,选择快乐,更需要勇气。 🍉 如果你也迷失在了路上,对人生充满了迷惘,不要害怕,冷静下来,

    2024年02月08日
    浏览(49)
  • 算法刷刷刷|动态规划篇|509.斐波那契数| 70.爬楼梯| 746.使用最小花费爬楼梯| 62.不同路径| 63不同路径2| 343.正数拆分 | 96.不同的二叉搜索树

    509. 斐波那契数 斐波那契数 (通常用 F(n) 表示)形成的序列称为 斐波那契数列 。该数列由 0 和 1 开始,后面的每一项数字都是前面两项数字的和。也就是: F(0) = 0,F(1) = 1 F(n) = F(n - 1) + F(n - 2),其中 n 1 给定 n ,请计算 F(n) 。 70.爬楼梯 746.使用最小花费爬楼梯 给你一个整数

    2023年04月23日
    浏览(56)
  • leetcode 823. 带因子的二叉树(dp+双指针+Long类型)

    题目表述 给出一个含有不重复整数元素的数组 arr ,每个整数 arr[i] 均大于 1。 用这些整数来构建二叉树,每个整数可以使用任意次数。其中:每个非叶结点的值应等于它的两个子结点的值的乘积。 满足条件的二叉树一共有多少个?答案可能很大,返回 对 1 0 9 + 7 10^9+7 1 0 9

    2024年02月10日
    浏览(37)
  • 96. 不同的二叉搜索树

    目录 1、题目描述 2、思路:动态规划 2.2、确定递推公式 2.3、dp数组初始化 2.4、确定遍历顺序 3、C++实现如下 给你一个整数 n ,求恰由 n 个节点组成且节点值从 1 到 n 互不相同的 二叉搜索树 有多少种?返回满足题意的二叉搜索树的种数。 示例 1: 输入:n = 3 输出:5 示例

    2024年02月02日
    浏览(68)
  • leetcode做题笔记96. 不同的二叉搜索树

    给你一个整数  n  ,求恰由  n  个节点组成且节点值从  1  到  n  互不相同的  二叉搜索树  有多少种?返回满足题意的二叉搜索树的种数。 本题与上题近似,只需分别考虑左右子树,即f[j-1]*f[i-j]得到状态方程,最后输出f[n[即可,或者直接使用公式,二叉搜索树的种数与

    2024年02月11日
    浏览(36)
  • leetcode做题笔记95. 不同的二叉搜索树 II

    给你一个整数  n  ,请你生成并返回所有由  n  个节点组成且节点值从  1  到  n  互不相同的不同  二叉搜索树   。可以按  任意顺序  返回答案。 本题要列出所有二叉搜索树,即可转换为列举左子树所有集合和右子树所有集合两个问题,分别用递归函数将左右子树根节

    2024年02月11日
    浏览(31)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包