Skip to content

Commit 830ad51

Browse files
committed
动态规划注释完善
1 parent ba8d641 commit 830ad51

22 files changed

+113
-75
lines changed

leetcode_Java/Solution0022.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,10 @@ private void dfs(String track, int n, int left, int right) {
4040
2、题目:数字n代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且有效的括号组合。
4141
3、题目简化:求n对括号的所有有效组合
4242
4、定义dp列表:dp[i]表示i对括号的所有有效组合。本题不能用一维或二维数组,而是用二维列表,存放的是字符串对象
43-
5、状态转移方程:dp[i] = "(" + dp[p]的组合 + ")" + dp[q]的组合,其中1+p+q=i,p从0遍历到i-1,q则相应从i-1到0
44-
6、初始化:dp[0]=[""], dp[1]=["()"]
43+
5、初始化:
44+
1)一维dp数组扩容:增加0对括号这一最小规模的情况
45+
2)dp[0]=[""], dp[1]=["()"]
46+
6、状态转移方程:dp[i] = "(" + dp[p]的组合 + ")" + dp[q]的组合,其中1+p+q=i,p从0遍历到i-1,q则相应从i-1到0
4547
7、遍历dp数组填表:从前向后,第一个for循环用于遍历dp列表未知位置,第二个for循环用于遍历dp列表已知位置,得到已知结果后根据状态转移方程推断计算未知结果并填表
4648
8、返回结果:最后一个状态就是结果
4749
*/

leetcode_Java/Solution0042.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,10 +62,12 @@ private int getMax(int[] height, int start, int end) {
6262
逐列累加雨水,动态规划,时间复杂度优化,空间换时间:
6363
1、为避免重复遍历计算某位置的左右最高柱子,使用dp数组存放最高柱子
6464
2、定义dp数组:dpLeft[i]表示i位置左边最高柱子的高度,dpRight[i]表示i位置右边最高柱子的高度
65-
3、状态转移方程:
65+
3、初始化:
66+
1)一维dp数组不用扩容
67+
2)dp[0]=0,不用赋值了
68+
4、状态转移方程:
6669
dpLeft[i] = Math.max(dpLeft[i - 1], height[i - 1]);
6770
dpRight[i] = Math.max(dpRight[i + 1], height[i + 1]);
68-
4、初始化:dp[0]=0,不用赋值了
6971
4、遍历dp数组填表:一个for循环遍历dp数组填表,求 左边最高柱子的高度 则 从左往右 遍历,求 右边最高柱子的高度 则 从右往左 遍历
7072
*/
7173
class Solution {

leetcode_Java/Solution0053.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,10 @@ public int maxSubArray(int[] nums) {
4646
1、题目:给你一个整数数组 nums,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。子数组是数组中的一个连续部分。
4747
2、题目简化:求数组nums的具有最大和的连续子数组的和。要先求多个局部连续子数组的最大和,再从多个局部最大中得到全局最大
4848
3、定义dp数组:dp[i]表示以索引i结尾的连续子数组的最大和
49-
4、状态转移方程:dp[i] = Math.max(nums[i], nums[i] + dp[i - 1]);
50-
5、初始化:dp[0] = nums[0];
49+
4、初始化:
50+
1)一维dp数组不用扩容,直接根据dp数组的定义就可以直观地对应进行初始化
51+
2)dp[0] = nums[0];
52+
5、状态转移方程:dp[i] = Math.max(nums[i], nums[i] + dp[i - 1]);
5153
6、遍历dp数组填表:一个for循环遍历dp数组,根据状态转移方程推断计算未知结果并填表
5254
7、返回结果:dp数组的最大值就是具有最大和的连续子数组的和
5355
*/

leetcode_Java/Solution0055.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@
66
1、题目:给定一个非负整数数组nums,你最初位于数组的第一个下标,数组中的每个元素代表你在该位置可以跳跃的最大长度,判断你是否能够到达最后一个下标。
77
2、题目简化:求索引 i 位置可以跳跃的最大长度,该长度是否大于等于最后一个索引位置
88
3、定义dp数组:dp[i]表示索引 i 位置能够跳跃到的最大长度
9-
4、状态转移方程:dp[i] = max(dp[i - 1], i + nums[i]);
10-
5、初始化:dp[0] = nums[0];
9+
4、初始化:
10+
1)dp数组不用扩容,直接根据dp数组的定义就可以直观地对应进行初始化
11+
2)dp[0] = nums[0];
12+
5、状态转移方程:dp[i] = max(dp[i - 1], i + nums[i]);
1113
6、遍历dp数组填表:一个for循环遍历dp数组,根据状态转移方程推断计算未知结果并填表
1214
7、返回结果:在状态转移前后进行跳跃长度判断以快速得出结果,没希望跳过去、已经足够跳过去
1315
*/

leetcode_Java/Solution0062.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@
66
1、题目:一个机器人位于一个 m x n 网格的左上角,机器人每次只能向下或者向右移动一步,机器人试图达到网格的右下角,问总共有多少条不同的路径?
77
2、题目简化:求从(0,0)到(m-1,n-1)的不同路径条数
88
3、定义dp数组:dp[i][j]表示到达位置(i,j)的不同路径条数
9-
4、状态转移方程:dp[i][j] = dp[i - 1][j] + dp[i][j - 1] 即上方和左方的不同路径条数相加
10-
5、初始化:首行首列只有一条路径,初始化为1
9+
4、初始化:
10+
1)二维dp数组不用扩容,直接根据dp数组的定义就可以直观地对应进行初始化
11+
2)首行首列只有一条路径,初始化为1
12+
5、状态转移方程:dp[i][j] = dp[i - 1][j] + dp[i][j - 1] 即上方和左方的不同路径条数相加
1113
6、遍历dp数组填表:两个for循环遍历二维dp数组每个位置,根据状态转移方程计算该位置不同路径条数并填表,直到终点,获得结果
1214
7、返回结果:最后一个状态就是结果
1315
*/

leetcode_Java/Solution0064.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@
66
1、题目:给定一个包含非负整数的 m x n 网格 grid ,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。说明:每次只能向下或者向右移动一步。
77
2、题目简化:求从 (0,0) 到达 (m-1,n-1) 位置的最小路径和
88
3、定义dp数组:本题可以直接在原数组操作。grid[i][j] 表示到达 (i,j) 位置的最小路径和
9-
4、状态转移方程:grid[i][j] += Math.min(grid[i - 1][j], grid[i][j - 1]); 即上方或左方的最小路径和
10-
5、初始化:首行首列的路径和进行累加初始化
9+
4、初始化:
10+
1)二维dp数组不用扩容,直接根据dp数组的定义就可以直观地对应进行初始化
11+
2)首行首列的路径和进行累加初始化
12+
5、状态转移方程:grid[i][j] += Math.min(grid[i - 1][j], grid[i][j - 1]); 即上方或左方的最小路径和
1113
6、遍历顺序:两个for循环遍历二维dp数组每个位置,根据状态转移方程计算该位置的最小路径和并填表,直到终点,获得结果
1214
*/
1315
class Solution {

leetcode_Java/Solution0121.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,14 @@
66
1、定义dp数组:
77
dp[i][0] 表示第i天交易结束后不持有股票的最大利润
88
dp[i][1] 表示第i天交易结束后持有股票的最大利润
9-
2、状态转移方程
9+
2、初始化
10+
dp[0][0] = 0 表示第0天不持有股票,最大利润为0。创建数组时默认为0,可省略
11+
dp[0][1] = -prices[0] 表示第0天持有股票,最大利润为-prices[0]
12+
3、状态转移方程
1013
dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][1] + prices[i]);
1114
第i天不持有股票 前一天不持有股票 前一天持有股票且今天卖出
1215
dp[i][1] = Math.max(dp[i - 1][1], -prices[i]); // 注意:只能买卖一次,所以买入时的利润为-prices[i]
1316
第i天持有股票 前一天持有股票 今天买入
14-
3、初始化
15-
dp[0][0] = 0 表示第0天不持有股票,最大利润为0。创建数组时默认为0,可省略
16-
dp[0][1] = -prices[0] 表示第0天持有股票,最大利润为-prices[0]
1717
4、遍历dp数组填表:一个for循环遍历数组,根据状态转移方程直接取dp数组的已知结果计算未知结果
1818
5、返回结果:最后一个不持有股票的状态就是结果
1919

leetcode_Java/Solution0122.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,14 @@
66
1、定义dp数组:
77
dp[i][0] 表示第i天交易结束后不持有股票的最大利润
88
dp[i][1] 表示第i天交易结束后持有股票的最大利润
9-
2、状态转移方程
9+
2、初始化
10+
dp[0][0] = 0 表示第0天不持有股票,最大利润为0。创建数组时默认为0,可省略
11+
dp[0][1] = -prices[0] 表示第0天持有股票,最大利润为-prices[0]
12+
3、状态转移方程
1013
dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][1] + prices[i]);
1114
第i天不持有股票 前一天不持有股票 前一天持有股票且今天卖出
1215
dp[i][1] = Math.max(dp[i - 1][1], dp[i - 1][0] - prices[i]); // 注意:可以买卖多次,所以买入时的利润包含之前所得利润,即dp[i - 1][0] - prices[i]
1316
第i天持有股票 前一天持有股票 前一天不持有股票且今天买入
14-
3、初始化
15-
dp[0][0] = 0 表示第0天不持有股票,最大利润为0。创建数组时默认为0,可省略
16-
dp[0][1] = -prices[0] 表示第0天持有股票,最大利润为-prices[0]
1717
4、遍历dp数组填表:一个for循环遍历数组,根据状态转移方程直接取dp数组的已知结果计算未知结果
1818
5、返回结果:最后一个不持有股票的状态就是结果
1919

leetcode_Java/Solution0139.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@
66
1、题目:给你一个字符串 s 和一个字符串列表 wordDict 作为字典。请你判断是否可以利用字典中出现的单词拼接出 s。注意:不要求字典中出现的单词全部都使用,并且字典中的单词可以重复使用。
77
2、题目简化:字符串s能否由单词列表拼出
88
3、定义dp数组:dp[i]表示前i个字符是否能由单词拼出
9-
4、状态转移方程:dp[i] = dp[j] && wordDict.contains(s.substring(j, i))
10-
5、初始化:dp[0] = true,表示字符串为空字符时的情况,方便递推判断
9+
4、初始化:
10+
1)一维dp数组扩容:增加空字符串这一最小规模的情况,方便初始化
11+
2)dp[0] = true,表示字符串为空字符时的情况,方便递推判断
12+
5、状态转移方程:dp[i] = dp[j] && wordDict.contains(s.substring(j, i))
1113
6、遍历dp数组填表:
1214
1)解释一:第一个for循环用于遍历dp数组未知位置,第二个for循环用于遍历dp数组已知位置,得到已知结果后根据状态转移方程推断计算未知结果并填表
1315
2)解释二:固定未知位置,由前面已知推断当前结果。固定第i个字符的位置,然后从头开始遍历到i位置,判断s[0,j)和s[j,i)是否能由单词拼出

leetcode_Java/Solution0152.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,12 @@ public int maxProduct(int[] nums) {
2929
1、题目:给你一个整数数组 nums ,请你找出数组中乘积最大的非空连续子数组(该子数组中至少包含一个数字),并返回该子数组所对应的乘积。
3030
2、题目简化:求数组的最大乘积
3131
3、定义dp数组:dpMax[i]表示以i位置结尾的子数组最大乘积,dpMin[i]表示以i位置结尾的子数组最小乘积。由于存在负数,因此要保留当前的最大最小值。
32-
4、状态转移方程:
32+
4、初始化:
33+
1)一维dp数组不用扩容,直接根据dp数组的定义就可以直观地对应进行初始化
34+
2)dpMax[0] = dpMin[0] = nums[0];
35+
5、状态转移方程:
3336
dpMax[i] = Math.max(nums[i], Math.max(dpMax[i - 1] * nums[i], dpMin[i - 1] * nums[i]));
3437
dpMin[i] = Math.min(nums[i], Math.min(dpMax[i - 1] * nums[i], dpMin[i - 1] * nums[i]));
35-
5、初始化:dpMax[0] = dpMin[0] = nums[0];
3638
6、遍历dp数组填表:一个for循环遍历dp数组的未知位置,根据状态转移方程直接取dp数组的已知结果计算未知结果
3739
7、返回结果:dpMax[i]中取最大值
3840
*/

leetcode_Java/Solution0188.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,14 @@
77
1)存在三种「状态」:天数、交易次数、是否持有股票,因此定义三维dp
88
2)dp[i][j][0] 表示第i天,已经进行了j次交易,不持有股票,获取的最大利润
99
dp[i][j][1] 表示第i天,已经进行了j次交易,持有股票,获取的最大利润
10-
2、状态转移方程
11-
dp[i][j][0] = Math.max(dp[i - 1][j][0], dp[i - 1][j][1] + prices[i]);
12-
dp[i][j][1] = Math.max(dp[i - 1][j][1], dp[i - 1][j - 1][0] - prices[i]);
13-
3、初始化:
10+
2、初始化:
1411
dp[0][j][0] = 0 表示第0天,不持有股票,任意交易次数,获取的最大利润都为0。创建数组时默认为0,可省略
1512
dp[0][j][1] = -prices[0] 表示第0天,持有股票,任意交易次数,获取的最大利润都为-prices[0]
13+
3、状态转移方程
14+
dp[i][j][0] = Math.max(dp[i - 1][j][0], dp[i - 1][j][1] + prices[i]);
15+
今天不持有股票 前一天不持有股票 前一天持有股票且今天卖出
16+
dp[i][j][1] = Math.max(dp[i - 1][j][1], dp[i - 1][j - 1][0] - prices[i]);
17+
今天持有股票 前一天持有股票 前一天不持有股票且今天买入
1618
4、遍历dp数组填表:第一层遍历天数,第二层遍历交易次数,根据状态转移方程填表。计算当前状态只跟前一天有关,所以两个for循环顺序先后都可以。
1719
5、返回结果:最后一个状态就是结果。即最后一天,已经进行了k次交易,不持有股票,获取的最大利润
1820
6、其他细节:

leetcode_Java/Solution0279.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,12 @@
66
1、题目:给你一个整数 n,返回 和为n 的完全平方数的最少数量。完全平方数是一个整数,其值等于另一个整数的平方;换句话说,其值等于一个整数自乘的积。例如,1、4、9 和 16 都是完全平方数,而 3 和 11 不是。
77
2、题目简化:求和为n的完全平方数的最少数量
88
3、定义dp数组:dp[i]表示和为i的完全平方数的最少数量
9-
4、状态转移方程:dp[i] = Math.min(dp[i], dp[i - j * j] + 1);
9+
4、初始化:
10+
1)一维dp数组扩容:增加和为0这一最小规模的情况
11+
2)dp[i] = i,表示最坏情况全部由1相加,数量为i
12+
5、状态转移方程:dp[i] = Math.min(dp[i], dp[i - j * j] + 1);
1013
1)j*j 是一个完全平方数,i-j*j 表示减去一个完全平方数的值,dp[i-j*j] 表示和为 i-j*j 完全平方数的最少数量,dp[i-j*j] + 1 表示和为i的完全平方数的数量
1114
2)构成i有多个完全平方数相加,所以遍历j,每次减去一个j*j的完全平方数,由旧状态加1直接得到当前值数量,比较得到最少数量
12-
5、初始化:dp[i] = i,表示最坏情况全部由1相加,数量为i
1315
6、遍历dp数组填表:一个for循环遍历dp数组的未知位置,另一个for循环遍历完全平方数,根据条件获取dp数组的已知位置,根据状态转移方程取已知结果汇总计算未知结果
1416
7、返回结果:最后一个状态就是结果
1517
*/

leetcode_Java/Solution0300.java

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,16 @@
22

33

44
/*
5-
题目简化:求数组的最长递增子序列的长度
6-
1、定义dp数组:dp[i] 表示以索引i的元素结尾的最长递增子序列的长度
7-
2、状态转移方程:if (nums[i] > nums[j]) dp[i] = Math.max(dp[i], dp[j] + 1);
5+
动态规划:
6+
1、题目简化:求数组的最长递增子序列的长度
7+
2、定义dp数组:dp[i] 表示以索引i的元素结尾的最长递增子序列的长度
8+
3、初始化:
9+
1)一维dp数组不用扩容,直接根据dp数组的定义就可以直观地对应进行初始化
10+
2)dp[i] = 1 表示每一个位置i的递增子序列的长度至少是1
11+
4、状态转移方程:if (nums[i] > nums[j]) dp[i] = Math.max(dp[i], dp[j] + 1);
812
位置i的最长递增子序列长度 等于j从0到i-1各个位置的 最长递增子序列长度加1的 最大值
9-
3、初始化:dp[i] = 1 表示每一个位置i的递增子序列的长度至少是1
10-
4、遍历dp数组填表:第一个for循环遍历dp数组的未知位置,第二个for循环遍历dp数组的已知位置,根据状态转移方程获取已知结果计算未知结果
11-
5、返回结果:遍历时比较每个位置结尾时的最长递增子序列的长度,并记录最大值,最后返回最大值
13+
5、遍历dp数组填表:第一个for循环遍历dp数组的未知位置,第二个for循环遍历dp数组的已知位置,根据状态转移方程获取已知结果计算未知结果
14+
6、返回结果:遍历时比较每个位置结尾时的最长递增子序列的长度,并记录最大值,最后返回最大值
1215
*/
1316
public class Solution {
1417
public int lengthOfLIS(int[] nums) {

leetcode_Java/Solution0309.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,13 @@
77
dp[i][0] 表示第i天交易结束后,持有股票,获取的最大利润
88
dp[i][1] 表示第i天交易结束后,不持有股票,处于冷冻期,获取的最大利润(第i天卖出股票后就处于冷冻期,则第i+1天不能买入股票)
99
dp[i][2] 表示第i天交易结束后,不持有股票,不处于冷冻期,获取的最大利润
10-
2、状态转移方程
10+
2、初始化
11+
dp[0][0] = -prices[0] 表示第0天交易结束后,持有股票,获取的最大利润为-prices[0]
12+
dp[0][1] = dp[0][2] = 0 表示第0天交易结束后,不持有股票,获取的最大利润为0。创建数组时默认为0,可省略
13+
3、状态转移方程
1114
dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][2] - prices[i]); // 今天持有股票。则前一天持有股票;或者前一天不持有股票、非冷冻期,且今天买入股票
1215
dp[i][1] = dp[i - 1][0] + prices[i]; // 今天不持有股票,处于冷冻期。则前一天持有股票,且今天卖出股票
1316
dp[i][2] = Math.max(dp[i - 1][1], dp[i - 1][2]); // 今天不持有股票,不处于冷冻期。则前一天不持有股票,处于冷冻期;或者前一天不持有股票,不处于冷冻期
14-
3、初始化
15-
dp[0][0] = -prices[0] 表示第0天交易结束后,持有股票,获取的最大利润为-prices[0]
16-
dp[0][1] = dp[0][2] = 0 表示第0天交易结束后,不持有股票,获取的最大利润为0。创建数组时默认为0,可省略
1717
4、遍历dp数组填表:一个for循环遍历数组,根据状态转移方程直接取dp数组的已知结果计算未知结果
1818
5、返回结果:第i天交易结束后,不持有股票的两个状态比较获取的最大利润
1919
*/

leetcode_Java/Solution0322.java

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -45,22 +45,24 @@ private int dfs(int[] memo, int[] coins, int amount) {
4545
1、题目:给你一个整数数组coins,表示不同面额的硬币;以及一个整数amount,表示总金额。计算并返回可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回-1。你可以认为每种硬币的数量是无限的。
4646
2、题目简化:求凑成总金额amount所需的最少硬币数
4747
3、定义dp数组:dp[i][j] 表示使用前i个硬币,凑成总金额j所需的最少硬币数
48-
4、状态转移方程
48+
4、初始化:
49+
1)二维dp数组扩容:增加0个硬币、总金额0这一最小规模的情况,方便初始化
50+
2)首列 dp[i][0] 表示使用前i个硬币,凑成总金额0所需的最少硬币数为0
51+
3)其他填充一个不可能的硬币数,使得不影响计算,也方便状态转移时取最小值
52+
5、状态转移方程
4953
if (j - coins[i - 1] >= 0) dp[i][j] = Math.min(dp[i - 1][j], dp[i][j - coins[i - 1]] + 1); // 金额足够,“不用”和“用”两种情况比较出最少
5054
else dp[i][j] = dp[i - 1][j]; // 金额不够,只能选择“不用”该硬币
5155
注意:dp[i - 1][j - coins[i - 1]] 表示第i个硬币只用一次,属于0-1背包。dp[i][j - coins[i - 1]] 表示第i个硬币重复使用,属于完全背包。
52-
5、初始化:
53-
1)首列 dp[i][0] 表示使用前i个硬币,凑成总金额j所需的最少硬币数为0
54-
2)其他填充一个不可能的硬币数,使得不影响计算,也方便状态转移时取最小值
5556
6、遍历dp数组填表:第一层遍历硬币,第二层遍历金额,都从1开始,正序遍历。两个for循环先后都可以,因为计算当前状态只需要当前行左边的状态和上一行的状态
5657
7、返回结果:最后一个状态就是结果
5758
5859
二维dp更新过程,从左到右,从上到下
5960
coins = [1, 2, 5], amount = 11
60-
0 12 12 12 12 12 12 12 12 12 12 12
61-
0 1 2 3 4 5 6 7 8 9 10 11
62-
0 1 1 2 2 3 3 4 4 5 5 6
63-
0 1 1 2 2 1 2 2 3 3 2 3
61+
0 1 2 3 4 5 6 7 8 9 10 11
62+
x 0 12 12 12 12 12 12 12 12 12 12 12
63+
1 0 1 2 3 4 5 6 7 8 9 10 11
64+
2 0 1 1 2 2 3 3 4 4 5 5 6
65+
5 0 1 1 2 2 1 2 2 3 3 2 3
6466
*/
6567
class Solution {
6668
public int coinChange(int[] coins, int amount) {

0 commit comments

Comments
 (0)