第九章 动态规划part02
今天开始逐渐有 dp的感觉了,前 两题 不同路径,可以好好研究一下,适合进阶
详细布置
62.不同路径
本题大家掌握动态规划的方法就可以。 数论方法 有点非主流,很难想到。
https://programmercarl.com/0062.%E4%B8%8D%E5%90%8C%E8%B7%AF%E5%BE%84.html
视频讲解:https://www.bilibili.com/video/BV1ve4y1x7Eu
63. 不同路径 II
https://programmercarl.com/0063.%E4%B8%8D%E5%90%8C%E8%B7%AF%E5%BE%84II.htmlhttps://programmercarl.com/0063.%E4%B8%8D%E5%90%8C%E8%B7%AF%E5%BE%84II.html
视频讲解:https://www.bilibili.com/video/BV1Ld4y1k7c6
343. 整数拆分 (可跳过)
本题思路并不容易想,一刷建议可以跳过。如果学有余力,可以看视频理解一波。
https://programmercarl.com/0343.%E6%95%B4%E6%95%B0%E6%8B%86%E5%88%86.html
视频讲解:https://www.bilibili.com/video/BV1Mg411q7YJ
96. .不同的二叉搜索树 (可跳过)
本题思路并不容易想,一刷建议可以跳过。 如果学有余力,可以看视频理解一波。
https://programmercarl.com/0096.%E4%B8%8D%E5%90%8C%E7%9A%84%E4%BA%8C%E5%8F%89%E6%90%9C%E7%B4%A2%E6%A0%91.html
视频讲解:https://www.bilibili.com/video/BV1eK411o7QA
62. 不同路径
题目链接
解题思路
1.确定dp数组以及下标i的含义
** dp[i][j] :表示从(0 ,0)出发,到(i, j) 有dp[i][j]条不同的路径**
** 2.确定递推公式**
** 想要求dp[i][j],只能有两个方向来推导出来,即dp[i - 1][j] 和 dp[i][j - 1]**
** dp[i][j]=dp[i][j-1]+dp[i-1][j];**
** 3.dp数组的初始化**
** 首先dp[i][0]一定都是1,因为从(0, 0)的位置到(i, 0)的路径只有一条,那么dp[0][j]也同理**
4.确定遍历顺序
5.举例推导dp数组
code
class Solution {
//时间复杂度:O(m × n) 空间复杂度O(m x n)
public int uniquePaths(int m, int n) {
//1.确定dp数组以及下标i的含义
//dp[i][j] :表示从(0 ,0)出发,到(i, j) 有dp[i][j]条不同的路径
//2.确定递推公式
//想要求dp[i][j],只能有两个方向来推导出来,即dp[i - 1][j] 和 dp[i][j - 1]
//dp[i][j]=dp[i][j-1]+dp[i-1][j];
//3.dp数组的初始化
//首先dp[i][0]一定都是1,因为从(0, 0)的位置到(i, 0)的路径只有一条,那么dp[0][j]也同理
int[][] dp=new int[m][n];
for(int i=0;i<n;i++){
dp[0][i]=1;
}
for(int j=0;j<m;j++){
dp[j][0]=1;
}
//4.确定遍历顺序
for(int i=1;i<m;i++){
for(int j=1;j<n;j++){
dp[i][j]=dp[i][j-1]+dp[i-1][j];
//System.out.println("i:"+i+",j:"+j+",res:"+dp[i][j]);
}
}
//5.举例推导dp数组
return dp[m-1][n-1];
}
}
来分析一下时间复杂度,这个深搜的算法,其实就是要遍历整个二叉树。
这棵树的深度其实就是m+n-1(深度按从1开始计算)。
那二叉树的节点个数就是 2^(m + n - 1) - 1。可以理解深搜的算法就是遍历了整个满二叉树(其实没有遍历整个满二叉树,只是近似而已)
所以上面深搜代码的时间复杂度为O(2^(m + n - 1) - 1),可以看出,这是指数级别的时间复杂度,是非常大的。
class Solution {
public int uniquePaths(int m, int n) {
return dfs(m-1,n-1,m,n);
}
public int dfs(int i,int j,int m,int n){
if(i<0 ||j <0){
return 0;
}
//找到了一种路径
if(i==0||j==0){
return 1;
}
return dfs(i-1,j,m,n)+dfs(i,j-1,m,n);
}
}
树递归的时间复杂度分析
这俩道题递归写法分析时间复杂度时是O(2n)和O(2(m + n - 1) - 1),斐波那契数 树深度是n,和最短路径 树深度是m+n-1 ,这个树深度是怎么理解得出的,有什么技巧嘛? 每次想不到,只能靠自己举例数下确实对上了,感觉没有理解到位。
:::tips
理解递归的时间复杂度O(n) O(logn):https://programmercarl.com/%E5%89%8D%E5%BA%8F/%E9%80%9A%E8%BF%87%E4%B8%80%E9%81%93%E9%9D%A2%E8%AF%95%E9%A2%98%E7%9B%AE%EF%BC%8C%E8%AE%B2%E4%B8%80%E8%AE%B2%E9%80%92%E5%BD%92%E7%AE%97%E6%B3%95%E7%9A%84%E6%97%B6%E9%97%B4%E5%A4%8D%E6%9D%82%E5%BA%A6%EF%BC%81.html
理解树深度:
深度优先遍历(Depth First Search, 简称_DFS) 与广度优先遍历(Breath First Search)是图论中两种非常重要的算法_
树深度理解的话,每一步可选的动作方向可以看成一个树的分支,而具体的走一步是一个树的节点。在不同路径里,很显然的一条路径是直接右走再下走,这对应于树中的一个根到叶子的路径,这条路径的深度是 m+n-1。在所有的深搜中,也大致都可以这样类比,每一步搜索时可选的值是分支,每个选择是一个节点,一个能够到达递归终止条件的选择集合就是一条路径
:::
63. 不同路径 II
题目链接
解题思路
跟上题差不都,就是多了些条件判断。
递推公式:
dp[i][j]=obstacleGrid[i][j]==1?0:(obstacleGrid[i-1][j]==1?0:dp[i-1][j])+(obstacleGrid[i][j-1]==1?0:dp[i][j-1]);
初始化要注意,遇到一个障碍物 后面都初始化为0
code
class Solution {
public int uniquePathsWithObstacles(int[][] obstacleGrid) {
int m=obstacleGrid.length;
int n=obstacleGrid[0].length;
int[][] dp=new int[m][n];
boolean falgm=false;
boolean falgn=false;
for(int i=0;i<m;i++){
if(falgm || obstacleGrid[i][0]==1){
dp[i][0]=0;
falgm=true;
}else{
dp[i][0]=1;
}
}
for(int j=0;j<n;j++){
if(falgn || obstacleGrid[0][j]==1){
dp[0][j]=0;
falgn=true;
}else{
dp[0][j]=1;
}
}
for(int i=1;i<m;i++){
for(int j=1;j<n;j++){
dp[i][j]=obstacleGrid[i][j]==1?0:(obstacleGrid[i-1][j]==1?0:dp[i-1][j])+(obstacleGrid[i][j-1]==1?0:dp[i][j-1]);
//System.out.println(dp[i][j]);
}
}
return dp[m-1][n-1];
}
}