LeetCode Hot100(65/100)——64. 最小路径和

文章目录

题目链接

LeetCode 64. 最小路径和

题目说明

给定一个包含非负整数的 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

约束条件:

  • m == grid.length
  • n == gridi.length
  • 1 <= m, n <= 200
  • 0 <= gridij <= 200

问题分析

这是一道经典的动态规划问题。我们需要从起点 (0,0) 走到终点 (m-1, n-1),每次只能向右或向下移动,目标是找到路径和最小的路径。
问题分析
路径限制
目标函数
解法思路
只能向右移动
只能向下移动
最小化路径和
动态规划
空间优化

解法一:二维动态规划

算法原理

使用二维数组 dp[i][j] 表示从起点 (0,0) 到达位置 (i,j) 的最小路径和。

状态转移方程:

  • 当 i = 0, j = 0 时:dp[0][0] = grid[0][0]
  • 当 i = 0 时(第一行):dp[0][j] = dp[0][j-1] + grid[0][j]
  • 当 j = 0 时(第一列):dp[i][0] = dp[i-1][0] + grid[i][0]
  • 其他情况:dp[i][j] = min(dp[i-1][j], dp[i][j-1]) + grid[i][j]

思路流程图

i,j
完成
开始
初始化dp数组
dp_0_0 = grid_0_0
填充第一行
填充第一列
遍历剩余单元格
dp_i_j = min

dp_i-1_j, dp_i_j-1

  • grid_i_j
    返回dp_m-1_n-1
    结束

Java代码实现

java 复制代码
class Solution {
    public int minPathSum(int[][] grid) {
        int m = grid.length;
        int n = grid[0].length;
        
        // 创建dp数组
        int[][] dp = new int[m][n];
        
        // 初始化起点
        dp[0][0] = grid[0][0];
        
        // 初始化第一行
        for (int j = 1; j < n; j++) {
            dp[0][j] = dp[0][j - 1] + grid[0][j];
        }
        
        // 初始化第一列
        for (int i = 1; i < m; i++) {
            dp[i][0] = dp[i - 1][0] + grid[i][0];
        }
        
        // 填充其余位置
        for (int i = 1; i < m; i++) {
            for (int j = 1; j < n; j++) {
                dp[i][j] = Math.min(dp[i - 1][j], dp[i][j - 1]) + grid[i][j];
            }
        }
        
        return dp[m - 1][n - 1];
    }
}

复杂度分析

  • 时间复杂度:O(m × n),需要遍历整个网格一次
  • 空间复杂度:O(m × n),需要额外的二维数组存储dp值

解法二:一维动态规划(空间优化)

算法原理

观察状态转移方程,我们发现计算 dp[i][j] 时只需要 dp[i-1][j](上方)和 dp[i][j-1](左方)的值。因此可以使用一维数组进行空间优化。

使用一维数组 dp[j] 表示当前行到达第 j 列的最小路径和。在更新过程中:

  • dp[j] 更新前存储的是上一行第 j 列的值(相当于 dp[i-1][j]
  • dp[j-1] 存储的是当前行第 j-1 列的值(相当于 dp[i][j-1]

优化思路图

二维数组 m×n
观察依赖关系
只依赖上方和左方
使用一维数组
空间复杂度 O_n_

Java代码实现

java 复制代码
class Solution {
    public int minPathSum(int[][] grid) {
        int m = grid.length;
        int n = grid[0].length;
        
        // 只需要一维数组
        int[] dp = new int[n];
        
        // 初始化第一行
        dp[0] = grid[0][0];
        for (int j = 1; j < n; j++) {
            dp[j] = dp[j - 1] + grid[0][j];
        }
        
        // 逐行更新
        for (int i = 1; i < m; i++) {
            // 更新当前行的第一列
            dp[0] = dp[0] + grid[i][0];
            
            // 更新当前行的其他列
            for (int j = 1; j < n; j++) {
                dp[j] = Math.min(dp[j], dp[j - 1]) + grid[i][j];
            }
        }
        
        return dp[n - 1];
    }
}

复杂度分析

  • 时间复杂度:O(m × n),仍需遍历整个网格
  • 空间复杂度:O(n),只需要长度为 n 的一维数组

解法三:原地修改(终极优化)

算法原理

如果允许修改原数组,可以直接在 grid 数组上进行动态规划,无需额外空间。

Java代码实现

java 复制代码
class Solution {
    public int minPathSum(int[][] grid) {
        int m = grid.length;
        int n = grid[0].length;
        
        // 初始化第一行
        for (int j = 1; j < n; j++) {
            grid[0][j] += grid[0][j - 1];
        }
        
        // 初始化第一列
        for (int i = 1; i < m; i++) {
            grid[i][0] += grid[i - 1][0];
        }
        
        // 填充其余位置
        for (int i = 1; i < m; i++) {
            for (int j = 1; j < n; j++) {
                grid[i][j] += Math.min(grid[i - 1][j], grid[i][j - 1]);
            }
        }
        
        return grid[m - 1][n - 1];
    }
}

复杂度分析

  • 时间复杂度:O(m × n)
  • 空间复杂度:O(1),不需要额外空间

解法对比

解法对比
二维DP
时间: O_m×n_
空间: O_m×n_
易理解
一维DP
时间: O_m×n_
空间: O_n_
空间优化
原地修改
时间: O_m×n_
空间: O_1_
修改原数组

示例演示

以示例 1 为例:grid = [[1,3,1],[1,5,1],[4,2,1]]

二维DP过程

复制代码
初始网格:          dp数组构建过程:
1  3  1           1  4  5
1  5  1    →      2  7  6
4  2  1           6  8  7

最小路径和 = 7
路径: 1→3→1→1→1

状态转移示例

(0,0)

值:1

路径和:1
(0,1)

值:3

路径和:4
(1,0)

值:1

路径和:2
(1,1)

值:5

路径和:7
(2,2)

值:1

路径和:7

总结

本题是动态规划的经典问题,核心在于找到状态转移方程。三种解法的时间复杂度相同,但空间复杂度逐步优化:

  1. 二维DP:最直观,适合初学者理解
  2. 一维DP:空间优化,实际应用推荐
  3. 原地修改:极致优化,但会修改原数组

在实际面试中,建议先说明二维DP思路,再提出空间优化方案,展现对算法的深入理解。

相关推荐
tiger865几秒前
大语言模型面试和题解,梳理中国主流开源 LLM 系列的发展脉络、技术路线与工程取舍
人工智能·gpt·深度学习·算法·自然语言处理·面试·transformer
我不会起名字32213 分钟前
一天一道力扣Hot100(36):深度优先算法---组合总和
数据结构·c++·后端·python·算法·go
虚无的纽扣40 分钟前
【力扣刷题】第三天:有效三角形的个数、快乐数
算法·排序算法
tiger86543 分钟前
大语言模型面试和题解,Context Engineering 怎么做?长 Prompt、动态上下文与 Memory 管理
人工智能·深度学习·算法·语言模型·自然语言处理·面试·nlp
不会就选b1 小时前
算法日常・每日刷题--<贪心>17
算法
yuanxi2001 小时前
上药集团药品销售公司签署沪明医疗数链协议:PSS⁵视角下的跨区域数据合作机会
职场和发展·创业创新·学习方法
随便发挥4 小时前
Leetcode 剑指 Offer II 186. 文物朝代判断
leetcode
6Hzlia4 小时前
【Classic 150 刷题计划】 LeetCode 205. 同构字符串 | C++ 双向绑定与哈希表映射
c++·算法·leetcode
有点。10 小时前
C++03阶段练习(练习题)
数据结构·算法·图论
周末也要写八哥12 小时前
经典算法实例:游戏中弱角色的数量(二)
算法