(LeetCode-Hot100)64. 最小路径和

64. 最小路径和

🔗 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

💡 解题思路

这是一个典型的**动态规划(Dynamic Programming)**问题。我们目标是从左上角 (0, 0) 到右下角 (m-1, n-1) 找出路径和最小的路径。

✅ 思路一:二维 DP(推荐)

步骤分解:
  1. 定义状态

    dp[i][j] 表示从 (0, 0)(i, j) 的最小路径和。

  2. 状态转移方程

    因为只能从上方或左方过来:

    复制代码
    dp[i][j] = grid[i][j] + min(dp[i-1][j], dp[i][j-1])
  3. 边界条件

    • 第一行只能从左边来:dp[0][j] = dp[0][j-1] + grid[0][j]
    • 第一列只能从上面来:dp[i][0] = dp[i-1][0] + grid[i][0]
  4. 初始化
    dp[0][0] = grid[0][0]

  5. 结果

    返回 dp[m-1][n-1]


✅ 思路二:空间优化的一维 DP

观察发现,计算当前行时只依赖上一行和当前行左侧的值,因此可以用一维数组优化空间。

  • 使用长度为 n 的数组 dp
  • 更新顺序:从左到右
  • 转移方程:
    • 对于第一行:dp[j] = dp[j-1] + grid[0][j]
    • 对于其他行:
      • dp[0] += grid[i][0](第一列)
      • dp[j] = grid[i][j] + min(dp[j], dp[j-1])

❌ 其他不推荐方法

  • DFS / BFS 暴力搜索:时间复杂度过高(指数级),会超时。
  • 记忆化递归:本质上等价于 DP,但常数较大,不如迭代清晰。

💻 代码实现

java 复制代码
// 方法一:二维 DP
class Solution {
    public int minPathSum(int[][] grid) {
        int m = grid.length;
        int n = grid[0].length;
        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] = grid[i][j] + Math.min(dp[i - 1][j], dp[i][j - 1]);
            }
        }
        
        return dp[m - 1][n - 1];
    }
}

// 方法二:一维 DP(空间优化)
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] += grid[i][0]; // 第一列只能从上边来
            for (int j = 1; j < n; j++) {
                dp[j] = grid[i][j] + Math.min(dp[j], dp[j - 1]);
            }
        }
        
        return dp[n - 1];
    }
}
go 复制代码
// 方法一:二维 DP
func minPathSum(grid [][]int) int {
    m, n := len(grid), len(grid[0])
    dp := make([][]int, m)
    for i := range dp {
        dp[i] = make([]int, n)
    }
    
    dp[0][0] = grid[0][0]
    
    // 初始化第一行
    for j := 1; j < n; j++ {
        dp[0][j] = dp[0][j-1] + grid[0][j]
    }
    
    // 初始化第一列
    for i := 1; i < m; i++ {
        dp[i][0] = dp[i-1][0] + grid[i][0]
    }
    
    // 填充其余位置
    for i := 1; i < m; i++ {
        for j := 1; j < n; j++ {
            dp[i][j] = grid[i][j] + min(dp[i-1][j], dp[i][j-1])
        }
    }
    
    return dp[m-1][n-1]
}

// 方法二:一维 DP(空间优化)
func minPathSum(grid [][]int) int {
    m, n := len(grid), len(grid[0])
    dp := make([]int, n)
    
    dp[0] = grid[0][0]
    
    // 初始化第一行
    for j := 1; j < n; j++ {
        dp[j] = dp[j-1] + grid[0][j]
    }
    
    // 从第二行开始更新
    for i := 1; i < m; i++ {
        dp[0] += grid[i][0] // 第一列只能从上边来
        for j := 1; j < n; j++ {
            dp[j] = grid[i][j] + min(dp[j], dp[j-1])
        }
    }
    
    return dp[n-1]
}

func min(a, b int) int {
    if a < b {
        return a
    }
    return b
}

💡 注意:Go 语言中需要手动实现 min 函数(Go 1.21+ 可用 slices.Min,但此处为通用性自定义)。


🧪 示例演示

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

二维 DP 表构建过程:

0 1 2
0 1 4 5
1 2 7 6
2 6 8 7
  • dp[0][0] = 1
  • dp[0][1] = 1+3=4, dp[0][2]=4+1=5
  • dp[1][0] = 1+1=2
  • dp[1][1] = 5 + min(2,4) = 7
  • dp[1][2] = 1 + min(7,5) = 6
  • dp[2][0] = 2+4=6
  • dp[2][1] = 2 + min(6,7) = 8
  • dp[2][2] = 1 + min(8,6) = 7

最终答案:7


✅ 答案有效性证明

我们可以用数学归纳法证明 DP 的正确性:

  • 基础情况dp[0][0] = grid[0][0] 显然正确。
  • 归纳假设 :假设对于所有 (i', j') 满足 i' < ij' < jdp[i'][j'] 已是最小路径和。
  • 归纳步骤 :到达 (i, j) 只能从 (i-1, j)(i, j-1) 来,根据归纳假设,这两个位置的值已是最优,因此取较小者加上 grid[i][j] 即为 (i, j) 的最优解。

因此,DP 状态转移正确,最终结果有效。


📊 复杂度分析

方法 时间复杂度 空间复杂度
二维 DP O(m × n) O(m × n)
一维 DP(优化) O(m × n) O(n)
  • 时间 :每个格子访问一次,共 m × n 次。
  • 空间:二维 DP 需要额外二维数组;一维 DP 只需一行空间,可原地优化(若允许修改原数组,甚至可 O(1) 空间)。

💡 实际面试中,建议先写二维 DP 清晰表达思路,再提出空间优化方案。


🔚 问题总结

  • 核心思想:动态规划,利用子问题最优解构造全局最优解。
  • 关键观察:只能向右或向下 → 当前状态仅依赖上方和左方。
  • 优化方向:从二维 DP 到一维 DP,空间从 O(mn) 降至 O(n)。
  • 适用场景:网格路径、最小/最大代价类问题(如不同路径、三角形最小路径和等)。

📌 延伸思考

  • 如果允许向四个方向移动?→ 需用 Dijkstra(带权最短路)。
  • 如果有障碍物?→ 在 DP 中跳过障碍位置即可(如 LeetCode 63)。

掌握此类 DP 模板,可快速解决大量网格路径优化问题!

github地址: https://github.com/swf2020/LeetCode-Hot100-Solutions

相关推荐
赫瑞10 分钟前
Java中的大数处理 —— BigInteger
java·开发语言
r_oo_ki_e_11 分钟前
java25--Collection集合
java·开发语言
色空大师13 分钟前
网站搭建实操(五)后台管理-短信模块
java·阿里云短信·网站·短信
极创信息17 分钟前
信创软件安全加固指南,信创软件的纵深防御体系
java·大数据·数据库·金融·php·mvc·软件工程
蜘蛛侠..30 分钟前
什么是 Plan-and-Execute 模式?与ReAct模式区别?
java·ai·大模型·llm·agent·react·plan模式
Mr_Xuhhh1 小时前
算法题解博客:三道经典题目的思路与实现
算法
算法-大模型备案 多米1 小时前
大模型备案实操指南:材料、流程与避坑要点
大数据·网络·人工智能·算法·文心一言
untE EADO1 小时前
SpringBoot:几种常用的接口日期格式化方法
java·spring boot·后端
顾温1 小时前
数据转换函数
开发语言·算法