题目:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台
一个机器人位于一个 m x n
网格的左上角 (起始点在下图中标记为 “Start” )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。
问总共有多少条不同的路径?
分析:
假设为3行2列的二维数组
1. 向右---向下---向下
2. 向下---向下---向右
3. 向下--向右---向下
可以推导
1. 一直向右和一直向下,每种走法只有1种走法,没有其他可以旋转的空间。因此
1 | 1 |
1 | 2 |
1 |
2 、那么到达dp[1][1] 的走法就是 1 + 1 = 2;
3. 那么到达右下角dp[2][1] 就是 2 + 1 = 3; 即3种走法
分析2:
假设有3行7列
1. 按行走,只有1种走法
2、按列走,只有一种走法,可得
1 | 1 | 1 | 1 | 1 | 1 | 1 |
1 | ||||||
1 |
那么dp[1][1] 就是 dp[0][1] + dp[1][0] 即 1+ 1 = 2;依次类推可得
1 | 1 | 1 | 1 | 1 | 1 | 1 |
1 | 2 | 3 | 4 | 5 | 6 | 7 |
1 |
第三行还是按照这种方式推导,可得
1 | 1 | 1 | 1 | 1 | 1 | 1 |
1 | 2 | 3 | 4 | 5 | 6 | 7 |
1 | 3 | 6 | 10 | 15 | 21 | 28 |
因此,针对3行7列的二维数组,可得28种走法
代码实现:
public int uniquePaths(int m, int n) {
int[][] dp = new int[m][n];
//第一行都为1
for (int col = 0; col < n; col++) {
dp[0][col] = 1;
}
//第一列都为1
for (int row = 0; row < m; row++) {
dp[row][0] = 1;
}
for (int row = 1; row < m; row++) {
for(int col = 1; col < n; col++) {
dp[row][col] = dp[row][col - 1] + dp[row - 1][col];
}
}
return dp[m-1][n-1];
}
还是一样的问题,以上代码的时间复杂度为O(m*n), 空间复杂度也为O(m*n). 如果100行100列的二维数组,将浪费100*100的空间复杂度。
空间压缩进行优化:
public int uniquePaths2(int m, int n) {
int[] dp = new int[n];
//第一行都为1
for (int col = 0; col < n; col++) {
dp[col] = 1;
}
for (int row = 1; row < m; row++) {
dp[0] = 1;
for (int col = 1; col < n; col++) {
dp[col] = dp[col -1] + dp[col];
}
}
return dp[n -1];
}
完整代码:
package code03.动态规划_07.lesson4;
//力扣62题
// https://leetcode.cn/problems/unique-paths/description/
public class DiffPathSum_02 {
public int uniquePaths(int m, int n) {
int[][] dp = new int[m][n];
//第一行都为1
for (int col = 0; col < n; col++) {
dp[0][col] = 1;
}
//第一列都为1
for (int row = 0; row < m; row++) {
dp[row][0] = 1;
}
for (int row = 1; row < m; row++) {
for(int col = 1; col < n; col++) {
dp[row][col] = dp[row][col - 1] + dp[row - 1][col];
}
}
return dp[m-1][n-1];
}
public int uniquePaths2(int m, int n) {
int[] dp = new int[n];
//第一行都为1
for (int col = 0; col < n; col++) {
dp[col] = 1;
}
for (int row = 1; row < m; row++) {
dp[0] = 1;
for (int col = 1; col < n; col++) {
dp[col] = dp[col -1] + dp[col];
}
}
return dp[n -1];
}
}
力扣测试通过:
题目:力扣63题: 力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台
一个机器人位于一个 m x n
网格的左上角 (起始点在下图中标记为 “Start” )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish”)。
现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径?
网格中的障碍物和空位置分别用 1
和 0
来表示。
分析:
1. 这一题其实跟上面一体解法相同,唯一的不同之处是有障碍物。
2. 第一行和第一列遇到障碍物,那么后面的路都走不通而已
3. 后面的推导,当前方格为障碍物,设置0代表走不通;如果不为0,则按照原有逻辑进行推导即可
完整代码:
package code03.动态规划_07.lesson4;
//力扣63题
// https://leetcode.cn/problems/unique-paths-ii/description/
public class DiffPathSum_03 {
//时间复杂度 O(m*n), 空间复杂度 O(m*n)
public int uniquePathsWithObstacles(int[][] obstacleGrid) {
int m = obstacleGrid.length;
int n = obstacleGrid[0].length;
//obstacleGrid[0][0] == 1 代表左上角第一个就是障碍物
//obstacleGrid[m-1][n-1] == 1 代表右下角最后一个是障碍物
if (obstacleGrid == null
|| obstacleGrid.length == 0
|| obstacleGrid[0][0] == 1
|| obstacleGrid[m-1][n-1] == 1) {
return 0;
}
int[][] dp = new int[m][n];
//处理第一行
dp[0][0] = 1;
for (int col = 1; col < n; col++) {
//如果当前列为1, 或者前一列为0. 代表遇到障碍物。后面路走不通,全部变为0
if (obstacleGrid[0][col] == 1 || dp[0][col -1] == 0) {
dp[0][col] = 0;
} else {
//第一行只有1条路
dp[0][col] = 1;
}
}
//处理第一列
for (int row = 1; row < m; row++) {
//如果当前列为1, 或者上一列为0. 代表遇到障碍物。后面路走不通,全部变为0
if (obstacleGrid[row][0] == 1 || dp[row-1][0] == 0) {
dp[row][0] = 0;
} else {
//第一行只有1条路
dp[row][0] = 1;
}
}
for (int row = 1; row < m; row++) {
for(int col = 1; col < n; col++) {
//如果当前列有障碍物,此条路走不通。当前列的值变为0
//否则,按照原有的逻辑进行计算
dp[row][col] = obstacleGrid[row][col] == 1 ? 0: dp[row][col - 1] + dp[row - 1][col];
}
}
return dp[m-1][n-1];
}
//空间压缩
//时间复杂度 O(m*n), 空间复杂度 O(n)
public int uniquePathsWithObstacles2(int[][] obstacleGrid) {
int m = obstacleGrid.length;
int n = obstacleGrid[0].length;
//obstacleGrid[0][0] == 1 代表左上角第一个就是障碍物
//obstacleGrid[m-1][n-1] == 1 代表右下角最后一个是障碍物
if (obstacleGrid == null
|| obstacleGrid.length == 0
|| obstacleGrid[0][0] == 1
|| obstacleGrid[m-1][n-1] == 1) {
return 0;
}
int[] dp = new int[n];
//处理第一行
dp[0] = 1;
for (int col = 1; col < n; col++) {
//如果当前列为1, 或者前一列为0. 代表遇到障碍物。后面路走不通,全部变为0
if (obstacleGrid[0][col] == 1 || dp[col -1] == 0) {
dp[col] = 0;
} else {
//第一行只有1条路
dp[col] = 1;
}
}
for (int row = 1; row < m; row++) {
//当前列为障碍物或者上一列为障碍物,都走不通。
dp[0] = (obstacleGrid[row][0] == 1 || dp[0] == 0) ? 0 : 1;
for(int col = 1; col < n; col++) {
//如果当前列有障碍物,此条路走不通。当前列的值变为0
//否则,按照原有的逻辑进行计算
dp[col] = obstacleGrid[row][col] == 1 ? 0 : dp[col -1] + dp[col];
}
}
return dp[n-1];
}
public static void main(String[] args) {
DiffPathSum_03 test = new DiffPathSum_03();
int[][] arr = {
{0, 0, 0},
{0, 1, 0},
{0, 0, 0}
};
System.out.println(test.uniquePathsWithObstacles2(arr));
}
}