【算法】动态规划之最小路径和

181 阅读3分钟

原题最小路径和

image.png

思路

记住路径的方向只能是向下或向右,因此:

  • 网格的第一行的每个元素只能从左上角元素开始向右移动到达
  • 网格的第一列的每个元素只能从左上角元素开始向下移动到达
  • 此时的路径是唯一的,因此每个元素对应的最小路径和即为对应的路径上的数字总和。

对于不在第一行和第一列的元素:

  • 可以从其上方相邻元素向下移动一步到达
  • 也从其左方相邻元素向右移动一步到达 元素对应的最小路径和等于【其上方相邻元素】与【其左方相邻元素】两者对应的最小路径和中的最小值加上当前元素的值。由于每个元素对应的最小路径和与其相邻元素对应的最小路径和有关,因此可以使用动态规划求解。

创建二维数组 dp,与原始网格的大小相同,dp[i][j] 表示从左上角出发到 (i,j) 位置的最小路径和。显然,dp[0][0]=grid[0][0]。对于 dp 中的其余元素,通过以下状态转移方程计算元素值。

当 i>0 且 j=0 时,dp[i][0]=dp[i−1][0]+grid[i][0]。

当 i=0 且 j>0 时,dp[0][j]=dp[0][j−1]+grid[0][j]。

当 i>0 且 j>0 时,dp[i][j]=min(dp[i−1][j],dp[i][j−1])+grid[i][j]。

最后得到 dp[m−1][n−1] 的值即为从网格左上角到网格右下角的最小路径和。

动态规划空间复杂度 O(mn)

 function minPathSum(grid: number[][]): number {

    if (grid == null || grid.length == 0 || grid[0].length == 0) {
        return 0
    }
    let rows = grid.length, columns = grid[0].length;
    const dp = Array.from({ length: rows }, () => Array(columns).fill(0));

    dp[0][0] = grid[0][0];


    // 如果是第一列的话, 每个元素只能向下移动
    for (let i = 1; i < rows; i++) {
        dp[i][0] = dp[i - 1][0] + grid[i][0]
    }

    // 如果是第一行的话, 每个元素只能向右移动
    for (let j = 1; j < columns; j++) {
        dp[0][j] = dp[0][j - 1] + grid[0][j]
    }

    for (let i = 1; i < rows; i++) {
        for (let 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]
};

复杂度分析

  • 时间复杂度: O(mn), m 是行的长度, n 是列的长度
  • 空间复杂度: O(mn), m 是行的长度, n 是列的长度

动态规划空间复杂度 O(n)

function minPathSum(grid: number[][]): number {

    if (grid == null || grid.length == 0 || grid[0].length == 0) {
        return 0
    }
    let columns = grid[0].length;
    let rows = grid.length
    const dp = new Array(columns).fill(0);

    dp[0] = grid[0][0];


    // 如果是第一行的话, 每个元素只能向右移动
    for (let j = 1; j < columns; j++) {
        dp[j] = dp[j - 1] + grid[0][j]
    }


   for (let i = 1; i < rows; i++) {
            //  如果是第一列的话,每个元素只能向下移动
            dp[0] += grid[i][0]; 
            for (let j = 1; j < columns; j++) {
                dp[j] = Math.min(dp[j], dp[j - 1]) + grid[i][j];
            }
        }


    return dp[columns - 1]
};

更改说明

* 一维数组 dp:使用一维数组 dp 来存储当前行的最小路径和。
* 初始化第一行:首先计算第一行的最小路径和,因为它只依赖于左边的元素。
* 更新每一行:对于每一行,从左到右更新 dp 数组。dp[j] 表示到达当前单元格的最小路径和,更新时考虑从上方和左方到达的路径。
* 空间优化:通过复用 dp 数组,空间复杂度从 O(m×n) 降低到 O(n)

复杂度

  • 时间复杂度: O( mn), m是行的长度,n 是列的长度
  • 空间复杂度: O(m), m 是行的长度

上方是列的维度,也可以从行的角度去思考