96.不同的二叉搜索树
给你一个整数
n
,求恰由n
个节点组成且节点值从1
到n
互不相同的 二叉搜索树 有多少种?返回满足题意的二叉搜索树的种数。示例 1:
输入:n = 3 输出:5示例 2:
输入:n = 1 输出:1提示:
1 <= n <= 19
dp[3] , 就是元素1为头节点的搜索树的数量+元素2为头节点的搜索树的数量+元素3为头节点的·搜索树的数量;
元素1为头节点的搜索树的数量=右子树有2个元素的搜索树数量 * 左子树有0个元素的搜索树数量;
元素2为头节点的搜索树的数量=左子树有1个元素的搜索树数量 * 右子树有1个元素的搜索树数量;
元素3为头节点的搜索树的数量=右子树有2个元素的搜索树数量 * 左子树有0个元素的搜索树数量;
有2个元素的搜索树数量就是dp[2];
有1个元素的搜索树数量就是dp[1];
有0个元素的搜索树数量就是dp[0];
所以dp[3]=dp[2]*dp[0] + dp[1] * dp[1] + dp[0] * dp[2]
动规五部曲:
1、确定dp数组以及下标的含义:1到i为节点的组成的二叉搜索树的个数为dp[i];
2、确定递推公式:dp[i] += dp[j - 1] * dp[i - j]; ,j-1 为j为头结点左子树节点数量,i-j 为以j为头结点右子树节点数量
3、dp数组如何初始化:dp[0]=1;
4、确定遍历顺序:从递推公式可以看出,节点数为i的状态是依靠i之前节点数的状态,所以是从前往后遍历;
5、举例推导dp数组:n为5的时候dp数组的状态:
for (int i = 2; i <= n; i++) {
for (int j = 1; j <= i; j++) {
//对于第i个节点,需要考虑1作为根节点直到i作为根节点的情况,所以需要累加
//一共i个节点,对于根节点j时,左子树的节点个数为j-1,右子树的节点个数为i-j
dp[i] += dp[j - 1] * dp[i - j];
}
}
综合代码:
// 定义一个Solution类
class Solution {
// 定义一个函数numTrees,参数为整数n,目的是计算n个节点的不同结构的二叉搜索树数量
public int numTrees(int n) {
// 初始化一个大小为n+1的整型数组dp,用于保存不同节点数量对应的二叉搜索树数量
int[] dp = new int[n + 1];
// 初始化0个节点和1个节点的情况,均为1,因为空树和只有一个节点的树都只有一种情况
dp[0] = 1;
dp[1] = 1;
// 循环遍历从2到n的所有节点数量
for (int i = 2; i <= n; i++) {
// 再次循环遍历每个节点数量对应的左子树和右子树节点数量的组合
for (int j = 1; j <= i; j++) {
// 对于第i个节点,需要考虑以1到i作为根节点的情况,所以需要累加
// 当根节点为j时,左子树的节点个数为j-1,右子树的节点个数为i-j
dp[i] += dp[j - 1] * dp[i - j];
}
}
// 返回n个节点的不同结构的二叉搜索树数量
return dp[n];
}
}
动态规划:01背包理论基础
46. 携带研究材料(第六期模拟笔试) (kamacoder.com)
代码随想录 (programmercarl.com)
带你学透0-1背包问题!| 关于背包问题,你不清楚的地方,这里都讲了!| 动态规划经典问题 | 数据结构与算法_哔哩哔哩_bilibili
题目描述
小明是一位科学家,他需要参加一场重要的国际科学大会,以展示自己的最新研究成果。他需要带一些研究材料,但是他的行李箱空间有限。这些研究材料包括实验设备、文献资料和实验样本等等,它们各自占据不同的空间,并且具有不同的价值。
小明的行李空间为 N,问小明应该如何抉择,才能携带最大价值的研究材料,每种研究材料只能选择一次,并且只有选与不选两种选择,不能进行切割。
输入描述
第一行包含两个正整数,第一个整数 M 代表研究材料的种类,第二个正整数 N,代表小明的行李空间。
第二行包含 M 个正整数,代表每种研究材料的所占空间。
第三行包含 M 个正整数,代表每种研究材料的价值。
输出描述
输出一个整数,代表小明能够携带的研究材料的最大价值。
输入示例
6 1 2 2 3 1 5 2 2 3 1 5 4 3
输出示例
5
提示信息
小明能够携带 6 种研究材料,但是行李空间只有 1,而占用空间为 1 的研究材料价值为 5,所以最终答案输出 5。
数据范围:
1 <= N <= 5000
1 <= M <= 5000
研究材料占用空间和价值都小于等于 1000
动规五部曲:
1、确定dp数组以及下标的含义:dp[i][j] 表示从下标为[o-i]的物品里任意取,放进容量为j的背包,价值总和最大是多少;
2、确定递推公式:不放物品i:dp[i-1][j] ;放物品i : 由dp[i - 1][j - weight[i]]推出,dp[i - 1][j - weight[i]] 为背包容量为j - weight[i]的时候不放物品i的最大价值,那么dp[i - 1][j - weight[i]] + value[i] (物品i的价值),就是背包放物品i得到的最大价值;
所以递归公式: dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);
3、dp数组的初始化;
4、确定遍历顺序’
5、举例推导dp数组;
综合代码:
public class BagProblem {
public static void main(String[] args) {
int[] weight = {1,3,4};
int[] value = {15,20,30};
int bagSize = 4;
testWeightBagProblem(weight,value,bagSize);
}
/**
* 动态规划获得结果
* @param weight 物品的重量
* @param value 物品的价值
* @param bagSize 背包的容量
*/
public static void testWeightBagProblem(int[] weight, int[] value, int bagSize){
// 创建dp数组
int goods = weight.length; // 获取物品的数量
int[][] dp = new int[goods][bagSize + 1];
// 初始化dp数组
// 创建数组后,其中默认的值就是0
for (int j = weight[0]; j <= bagSize; j++) {
dp[0][j] = value[0];
}
// 填充dp数组
for (int i = 1; i < weight.length; i++) {
for (int j = 1; j <= bagSize; j++) {
if (j < weight[i]) {
/**
* 当前背包的容量都没有当前物品i大的时候,是不放物品i的
* 那么前i-1个物品能放下的最大价值就是当前情况的最大价值
*/
dp[i][j] = dp[i-1][j];
} else {
/**
* 当前背包的容量可以放下物品i
* 那么此时分两种情况:
* 1、不放物品i
* 2、放物品i
* 比较这两种情况下,哪种背包中物品的最大价值最大
*/
dp[i][j] = Math.max(dp[i-1][j] , dp[i-1][j-weight[i]] + value[i]);
}
}
}
// 打印dp数组
for (int i = 0; i < goods; i++) {
for (int j = 0; j <= bagSize; j++) {
System.out.print(dp[i][j] + "\t");
}
System.out.println("\n");
}
}
}