一.题目描述
给定一个包含非负整数的 m x n 网格 grid ,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。
说明:每次只能向下或者向右移动一步。 输入:grid = [[1,3,1],[1,5,1],[4,2,1]] 输出:7 解释:因为路径 1→3→1→1→1 的总和最小。 示例 2:
输入:grid = [[1,2,3],[4,5,6]] 输出:12
二.题目解析
1.记忆化递归
public int minPathSum(int[][] grid) {
return minPathSum(grid, grid.length - 1, grid[0].length - 1, new HashMap<String, Integer>());
}
public int minPathSum(int[][] grid, int i, int j, Map<String, Integer> map) {
if (i == 0 && j == 0) {
return grid[i][j];
}
String key = i + "*" + j;
if (map.containsKey(key)) {
return map.get(key);
}
int res = 0;
//第一行只能从左边走过来
if (i == 0) {
res = grid[i][j] + minPathSum(grid, i, j - 1, map);
}else if (j == 0) {
//第一列只能从上面走下来
res = grid[i][j] + minPathSum(grid, i - 1, j, map);
}else {
//取从上面走下来和从左边走过来的最小值+当前坐标的值
res = grid[i][j] + Math.min(minPathSum(grid, i - 1, j, map), minPathSum(grid, i, j - 1, map));
}
map.put(key, res);
return res;
}
2.动态规划,二维dp数组
public int minPathSum1(int[][] grid) {
/*动态规划.dp[i][j]表示左上角到(i,j)格子的最小距离
时间复杂度O(mn),空间复杂度O(mn)
* */
if(grid == null || grid.length == 0){
return 0;
}
int width = grid[0].length;
int height = grid.length;
int[][] dp = new int[height][width];
//初始化左上角格子的dp值
dp[0][0] = grid[0][0];
//第一行的元素只能从其左侧元素而来
for (int i = 1; i < width; i++) {
dp[0][i] = dp[0][i - 1] + grid[0][i];
}
//第一列的元素只能由其上侧元素而来
for (int i = 1; i < height; i++) {
dp[i][0] = dp[i - 1][0] + grid[i][0];
}
for (int i = 1; i < height; i++) {
for (int j = 1; j < width; j++) {
//每一个格子只能由其上侧元素或者左侧元素而来
dp[i][j] = Math.min(dp[i - 1][j],dp[i][j - 1]) + grid[i][j];
}
}
return dp[height - 1][width - 1];
}
3.动态规划,一维dp数组
public int minPathSum3(int[][] grid) {
/*动态规划.dp[i][j]表示左上角到(i,j)格子的最小距离
时间复杂度O(mn),空间复杂度O(m)(二维变一维)
1 3 1 [1]-> [4]-> [5]
1 5 1 ----> [2]-> [5]-> [6]
4 2 1 [6]-> [7]-> [7(最终结果)]
* */
if(grid == null || grid.length == 0){
return 0;
}
int width = grid[0].length;
int height = grid.length;
int[] dp = new int[height];
//初始化左上角格子的dp值
dp[0] = grid[0][0];
//初始化第一列的值
for (int i = 1; i < height; i++) {
dp[i] = dp[i - 1] + grid[i][0];
}
for (int i = 1; i < width; i++) {
for (int j = 0; j < height; j++) {
//考虑第一行的格子比较特殊,只能从其左侧元素而来
dp[j] = j - 1 >= 0 ? Math.min(dp[j - 1],dp[j]) + grid[j][i] : dp[j] + grid[j][i];
}
}
return dp[height - 1];
}
4.动态规划,若允许修改原数组,空间复杂度可降为O(1)
public int minPathSum2(int[][] grid) {
/*动态规划.dp[i][j]表示左上角到(i,j)格子的最小距离
时间复杂度O(mn),空间复杂度O(1)(直接在原数组上面操作)
* */
if(grid == null || grid.length == 0){
return 0;
}
int width = grid[0].length;
int height = grid.length;
//无需初始化左上角格子的dp值
//第一行的元素只能从其左侧元素而来
for (int i = 1; i < width; i++) {
grid[0][i] = grid[0][i - 1] + grid[0][i];
}
//第一列的元素只能由其上侧元素而来
for (int i = 1; i < height; i++) {
grid[i][0] = grid[i - 1][0] + grid[i][0];
}
for (int i = 1; i < height; i++) {
for (int j = 1; j < width; j++) {
//每一个格子只能由其上侧元素或者左侧元素而来
grid[i][j] = Math.min(grid[i - 1][j],grid[i][j - 1]) + grid[i][j];
}
}
return grid[height - 1][width - 1];
}
|