#64
中等
低频
动态规划最小路径和
这是一道围绕算法展开的高频练习。建议先掌握「动态规划」这套写法,再结合下方步骤讲解理解状态维护、边界处理和复杂度取舍。
算法
题目分析
这道题表面上是在处理「最小路径和」,但先要想清楚题目到底让你返回什么,以及过程中哪些约束必须一直满足。从题型上看,它主要在考 算法 这些能力。这类题先别急着写转移式,先定义清楚状态表示什么,再看它如何由前面的状态推出。只有先把题目要求翻译成人话,后面的推荐代码才是在实现思路,而不是直接给答案。
接下来怎么看推荐代码: 带着这个理解再看推荐代码时,重点观察这条主线:用 DP 记录到每个格子的最小路径和,状态来自上方和左方较小者加当前值。
推荐代码
推荐解法:动态规划
时间复杂度: O(m*n)
空间复杂度: O(m*n)
核心思路: 用 DP 记录到每个格子的最小路径和,状态来自上方和左方较小者加当前值。
class Solution {
/**
* 计算从左上角到右下角的最小路径和
* @param grid 包含非负整数的二维网格
* @return 最小路径和
*/
public int minPathSum(int[][] grid) {
// 边界条件检查
if (grid == null || grid.length == 0 || grid[0].length == 0) {
return 0;
}
int rows = grid.length; // 网格行数
int columns = grid[0].length; // 网格列数
int[][] dp = new int[rows][columns]; // dp数组,存储到每个位置的最小路径和
// 初始化起点
dp[0][0] = grid[0][0];
// 初始化第一列
for (int i = 1; i < rows; i++) {
dp[i][0] = dp[i - 1][0] + grid[i][0];
}
// 初始化第一行
for (int j = 1; j < columns; j++) {
dp[0][j] = dp[0][j - 1] + grid[0][j];
}
// 填充其他位置
for (int i = 1; i < rows; i++) {
for (int j = 1; j < columns; j++) {
// 选择从左边或上边来的较小路径和
dp[i][j] = Math.min(dp[i - 1][j], dp[i][j - 1]) + grid[i][j];
}
}
// 返回右下角的最小路径和
return dp[rows - 1][columns - 1];
}
}
结构化讲解
面试时怎么讲
开场思路
这题我会用网格 DP,因为移动方向固定,只依赖上方和左方。
核心过程
- 定义状态为到每个格子的最小路径和。
- 先初始化起点、第一行和第一列。
- 内部格子从上方和左方的较小代价转移过来。
- 最终右下角就是答案。
复杂度总结
时间复杂度 O(m*n),空间复杂度 O(m*n)。
面试补一句:网格 DP 通常先问自己:到当前格子最后一步能从哪来。
核心思路
最小路径和是典型网格 DP。因为每一步只能向右或向下,所以到达某个格子的最优路径只可能来自上方或左方。
步骤讲解
1
定义到每个格子的最小代价
令 dp[i][j] 表示从左上角走到 (i,j) 的最小路径和。
为什么这样做:这样终点答案自然落在右下角。
对应代码提示:dp[i][j]
2
初始化第一行和第一列
边界只能单方向到达,所以路径和只能从前一个格子累加过来。
为什么这样做:第一行没有上方,第一列没有左方。
对应代码提示:dp[i][0] = dp[i - 1][0] + grid[i][0];
3
内部格子取上左较小值
内部位置的最优值等于 min(dp[i-1][j], dp[i][j-1]) + grid[i][j]。
为什么这样做:走到当前格子之前,最后一步只可能来自上或左。
对应代码提示:dp[i][j] = Math.min(dp[i - 1][j], dp[i][j - 1]) + grid[i][j];
易错点
忘记初始化第一行第一列
会让后续状态引用未定义值。
正确理解:边界单独处理,再统一转移内部格子。
把它当成可四向移动
题目只允许向右和向下,状态依赖非常简单。
正确理解:转移只看上方和左方。
直接修改原网格却没说明
虽然能做空间优化,但容易让实现和解释混乱。
正确理解:先用独立 DP 表解释,再视需要优化。
复杂度与适用判断
时间复杂度:O(m*n)
空间复杂度:O(m*n)
比其他方案更好在哪里:比 DFS 搜索所有路径高效得多,也更直观。
适用判断:网格题若移动方向受限且求最值,优先考虑二维 DP。
额外提醒
- 最后一步来源决定了这类网格 DP 的状态转移。
动画演示
如果你还没完全看懂,可以展开动画演示。
动态规划动画演示
想看这段解法是怎么一步步运行的,可以展开动画继续观察。
动态规划动画演示
想看这段解法是怎么一步步运行的,可以展开动画继续观察。
准备就绪 - 输入网格值(用分号分隔行,逗号分隔列),然后点击开始
动画演示仅在桌面端提供,移动端请优先阅读推荐代码与结构化讲解。