LeetCode:62. 不同路径 - 力扣(LeetCode)
1.思路
当前位置的路径数是上头上一个和左侧一个的和,所以可以一点点推导,想到动规。
动规五部曲:
①确定dp[i][j]数组定义:到达(i, j)处有 dp[i][j] 种路径 或 到达第 i 行,第 j 列有 dp[i][j] 种路径;
②确定递推公式:dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
③初始化:
行初始化:每列的首位数初始化为 1,也即dp[i][0];
列初始化:每行的首位数初始化为 1 ,也即dp[0][j];
④确定遍历顺序:先行后列,和dp数组的定义有关
⑤打印dp数组:好懒。。
小问题:dp[][]数组的大小边界怎么明确?遍历顺序怎么更清晰?做题的框架感得更清晰
2.代码实现
class Solution {
public int uniquePaths(int m, int n) {
// 到达(i, j)处有 dp[i][j] 种路径 或 到达第 i 行,第 j 列有 dp[i][j] 种路径
int[][] dp = new int[m][n];
// 初始化
// 列初始化:每行的首位数初始化为 1
for (int i = 0; i < m; i++) {
dp[i][0] = 1;
}
// 行初始化:每列的首位数初始化为 1
for (int i = 0; i < n; i++) {
dp[0][i] = 1;
}
// 递推公式 dp[i][j] = dp[i - 1][j] + dp[i][j - 1]
// 遍历顺序:先行后列(和dp数组的声明有关)
for (int i = 1; i < m; i++) { // 行
for (int j = 1; j < n; j++) { // 列
dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
}
}
return dp[m - 1][n - 1];
}
}
3.复杂度分析
时间复杂度:O(n * m).
空间复杂度:O(n).
LeetCode:63. 不同路径 II - 力扣(LeetCode)
1.思路
在上一题的基础上,增加了中介障碍,在动规过程都要考虑这个障碍,有障碍和无障碍的赋值,有障碍直接赋值为0,表示到不了,无障碍就是头上一个和左侧一个值的和。 动规五部曲之四步:
①确定dp[i][j]数组的含义,到达(i, j)位置所具有的路径数。
②确定动规公式:dp[i][j] = (obstacle[i][j] == 0) ? dp[i - 1][j] + dp[i][j - 1] : 0
③初始化:初始化首行和首列,没有障碍的地方全部初始化为1,有障碍的默认为0.
④单层动规逻辑:先行后列,和dp[][]数组定义有关
⑤(没有)打印dp[][]数组
2.代码实现
class Solution {
public int uniquePathsWithObstacles(int[][] obstacleGrid) {
int m = obstacleGrid.length;
int n = obstacleGrid[0].length;
// dp数组
int[][] dp = new int[m][n]; // 大小怎么定?
// 首尾节点为障碍的边界值排除
if (obstacleGrid[m - 1][n - 1] == 1 || obstacleGrid[0][0] == 1) {
return 0;
}
// 初始化
// 列初始化:每行的首个位置没有障碍时均为 1
for (int i = 0; i < m && obstacleGrid[i][0] == 0; i++) {
dp[i][0] = 1;
}
// 行初始化:每列的首个位置没有障碍时均为 1
for (int i = 0; i < n && obstacleGrid[0][i] == 0; i++) {
dp[0][i] = 1;
}
// 动规遍历顺序:先行后列
for (int i = 1; i < m; i++) {
for (int j = 1; j < n; j++) {
dp[i][j] = (obstacleGrid[i][j] == 0) ? dp[i -1][j] + dp[i][j - 1] : 0;
}
}
return dp[m - 1][n - 1];
}
}
3.复杂度分析
时间复杂度:O(n * m).
空间复杂度:O(n).