这是我参与8月更文挑战的第18天,活动详情查看:8月更文挑战
前言
关于 LeetCode 数组类型题目的相关解法,可见LeetCode 数组类型题目做前必看,分类别解法总结了题目,可以用来单项提高。觉得有帮助的话,记得多多点赞关注哦,感谢!
题目描述
给定一个包含非负整数的 m x n 网格 grid ,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。
说明:每次只能向下或者向右移动一步。
示例 1:
输入: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
链接:leetcode-cn.com/problems/mi…
题解
-
记忆化递归. 同上一道题Unique Paths II 一样, 可以利用递归的方法求解, path[i][j] = min(path[i-1][j], path[i][j-1]) + grid[i][j], 因为在求解过程中, 存在同一个值会被多次计算的情况, 比如 path[i][j-1] 除了在计算 path[i][j] 中被用到, 也会在计算 path[i+1][j-1] (path[i+1][j-1] = path[i][j-1] + path[i][j-2]) 中用到, 因此, 为了避免多次计算同一个值, 我们可以利用一个记忆数组来保存已经计算过的值, 这样, 在下一次用到该值时, 就可以直接取出来, 而不是重复计算.
具体解法见如下代码, 其中 path 数组就代表记忆数组, 当对应的元素为 -1 时, 就表示还未计算过, 当不为 -1 时, 表示已经计算过, 可以直接返回.
时间复杂度 O(mn), 空间复杂度 O(mn)
/**
* @param {number[][]} grid
* @return {number}
*/
var minPathSum = function(grid) {
const m = grid.length
const n = grid[0].length
// 记忆数组
const path = Array.from({ length: m }, () => (new Array(n).fill(-1)))
const minPath = (x, y) => {
// 到了起点
if (x === 0 && y === 0) return grid[0][0]
// 出了边界
if (x < 0 || y < 0) return Number.MAX_VALUE
// 记忆数组中对应位置已经计算过值了
if (path[x][y] !== -1) {
return path[x][y]
}
// 递归
path[x][y] = grid[x][y] + Math.min(minPath(x-1, y), minPath(x, y-1))
return path[x][y]
}
return minPath(m-1, n-1)
};
- 动态规划. 一般来讲, 如果一个问题能用记忆化递归的方法来解决, 那么该问题也能用动态规划的问题来解决. 记忆化递归是从尾到头的方式, 而动态规划是从头到尾的方式. 什么意思呢? 就是我们从起点开始计算, path[0][0] = grid[0][0], 边界上的点有 path[i][0] = path[i-1][0] + grid[i][0], path[0, j] = path[0, j-1] + grid[0, j], 那么内部的点有 path[i][j] = min(path[i-1][j] + path[i][j-1]) + grid[i][j], 这样就实现了从头到尾的方式.
具体代码见下方, 需要注意的一个点是, 我们用原 grid 数组来当作了 path 数组, 这样可以减少空间复杂度.
时间复杂度 O(mn), 空间复杂度 O(1)
/**
* @param {number[][]} grid
* @return {number}
*/
var minPathSum = function(grid) {
let m = grid.length;
if (m == 0) return 0;
let n = grid[0].length;
for (let i = 0; i < m; ++i) {
for (let j = 0; j < n; ++j) {
if (i === 0 && j === 0) continue;
if (i === 0)
grid[i][j] += grid[i][j-1]
else if (j === 0)
grid[i][j] += grid[i-1][j]
else
grid[i][j] += Math.min(grid[i][j-1], grid[i-1][j])
}
}
return grid[m - 1][n - 1];
};