系列文章目录
目录
- 系列文章目录
- 343. 整数拆分
- 动态规划
- 96.不同的二叉搜索树
- 动态规划
343. 整数拆分
动态规划
动规五部曲:
- 确定
dp
数组(dp table)以及下标的含义:dp[i]
表示分拆数字i
,可以得到的最大乘积为dp[i]
。 - 确定递推公式:
dp[i] = Math.max(dp[i], Math.max(j * (i - j), j * dp[i - j]));
dp
的初始化:dp[2] = 1;
- 确定遍历顺序:
dp[i]
是依靠dp[i - j]
的状态,所以遍历i
一定是从前向后遍历,先有dp[i - j]
再有dp[i]
。 - 举例推导
dp
数组。
class Solution {public int integerBreak(int n) {int[] dp = new int[n + 1];//dp数组初始化dp[2] = 1;for (int i = 3; i <= n; i++) {for (int j = 1; j <= i - j; j++) {// 这里的 j 其实最大值为 i-j,再大只不过是重复而已,即j<=i/2//并且,在本题中,我们分析 dp[0], dp[1]都是无意义的,//j 最大到 i-j,就不会用到 dp[0]与dp[1]dp[i] = Math.max(dp[i], Math.max(j * (i - j), j * dp[i - j]));// j * (i - j) 是单纯的把整数 i 拆分为两个数 也就是 i,i-j ,再相乘//而j * dp[i - j]是将 i 拆分成两个以及两个以上的个数,再相乘。//在每次遍历中(即第二个for循环)更新 dp[i],确保其始终为最大值,因每次循环都会得到一个dp[i]。}}return dp[n];}
}
96.不同的二叉搜索树
动态规划
动规五部曲:
- 确定
dp
数组(dp table)以及下标的含义:dp[i]
表示1
到i
为节点组成的二叉搜索树的个数为dp[i]
;也可以理解是i
个不同元素节点组成的二叉搜索树的个数为dp[i]
。 - 确定递推公式:
dp[i] += dp[以j为头结点左子树节点数量] * dp[以j为头结点右子树节点数量]
,j
相当于是头结点的元素,从1
遍历到i
为止。所以递推公式:dp[i] += dp[j - 1] * dp[i - j];
,j-1
为j
为头结点左子树节点数量,i-j
为以j
为头结点右子树节点数量。 dp
数组如何初始化:只需要初始化dp[0]
就可以了,推导的基础,都是dp[0]
。从定义上来讲,空节点也是一棵二叉树,也是一棵二叉搜索树,这是可以说得通的。从递归公式上来讲,dp[以j为头结点左子树节点数量] * dp[以j为头结点右子树节点数量]
中以j
为头结点左子树节点数量为0
,也需要dp[以j为头结点左子树节点数量] = 1
, 否则乘法的结果就都变成0
了。所以初始化dp[0] = 1
。- 确定遍历顺序:首先一定是遍历节点数,从递归公式:
dp[i] += dp[j - 1] * dp[i - j]
可以看出,节点数为i
的状态是依靠i
之前节点数的状态。所以是从前往后遍历。 - 举例推导
dp
数组。
class Solution {public int numTrees(int n) {int[] dp = new int[n + 1];dp[0] = 1;for (int i = 1; i <= n; i++) {//整数i代表有i个元素for (int j = 1; j <= i; j++) {//以j为头节点的二叉搜索树//对于i个节点,需要考虑1作为根节点直到i作为根节点的情况,所以需要累加//一共i个节点,对于根节点j时,左子树的节点个数为j-1,右子树的节点个数为i-jdp[i] += dp[j - 1] * dp[i - j];//以j为头节点的左子树节点数量*以j为头节点的右子树节点数量}}return dp[n];}
}