最小路徑和
題目
給定一個包含非負整數的 m x n 網格,請找出一條從左上角到右下角的路徑,使得路徑上的數字總和爲最小。
說明:每次只能向下或者向右移動一步。
示例
輸入:
[
[1,3,1],
[1,5,1],
[4,2,1]
]
輸出: 7
解釋: 因爲路徑 1→3→1→1→1 的總和最小。
解題
分析
這道題,我能想到的一共三種解法。
首先,這很類似與迷宮的問題,很顯然可以使用遞歸來解決,我們可以把所有可能到達右下角最後一個元素的路線的權值和都計算出來,然後取最小的一個。可以優化在遞歸的過程中,就記錄最小值,某條路徑遞歸的一半就比這個最小值大或者小,那麼我們就終止遞歸。
這種方式的時間複雜度爲O(2^m+n),空間複雜度爲爲O(m+n)
第二種,這道題很顯然是一個動態規劃的問題,到達最後一個元素(n,m)的最短路徑,要取決於到達(n - 1,m)和(n,m - 1)的元素的最短路徑,如果要求不破壞原有的數據,我們可以再定義一個同樣大小的二維數組,從最後一行開始,從後往前遍歷所有的元素,每一個元素都記錄右下角到這個節點的最短路徑,最後返回(0,0)個元素就可以了。(一維數組也可以實現)
這種方式的時間複雜度爲O(mn),空間複雜度爲爲O(mn)(一維數組的話空間複雜度爲O(m))
如果不需要保留原來的數據結構,我們可以在題目中的二維數組直接修改,這樣空間複雜度爲O(1)
代碼
第一種-遞歸優化版本
class Solution {
// 記錄當前的最小值
int min = -1;
public int minPathSum(int[][] grid) {
recursive(grid, 0, 0, grid[0][0]);
return min;
}
private void recursive(int[][] grid, int i, int j, int sum) {
// 中斷遞歸的條件,否則會超時
if (min != -1 && sum >= min) {
return;
}
if (i == grid.length - 1 && j == grid[0].length - 1) {
min = sum;
return;
}
// 優先向下走
if (i < grid.length - 1) {
recursive(grid, i + 1, j, sum + grid[i + 1][j]);
}
// 上路走不通再向下走
if (j < grid[0].length - 1) {
recursive(grid, i, j + 1, sum + grid[i][j + 1]);
}
}
}
第二種-二維數組動態規劃
class Solution {
public int minPathSum(int[][] grid) {
int[][] dp = new int[grid.length][grid[0].length];
for (int i = grid.length - 1; i >= 0; i--) {
for (int j = grid[0].length - 1; j >= 0; j--) {
if (i < grid.length - 1 && j < grid[0].length - 1) {
dp[i][j] = Math.min(dp[i + 1][j], dp[i][j + 1]) + grid[i][j];
} else if (i == grid.length - 1 && j < grid[0].length - 1) {
dp[i][j] = dp[i][j + 1] + grid[i][j];
} else if (i < grid.length - 1 && j == grid[0].length - 1) {
dp[i][j] = dp[i + 1][j] + grid[i][j];
} else {
dp[i][j] = grid[i][j];
}
}
}
return dp[0][0];
}
}
第三種-動態規劃-原地修改
class Solution {
public int minPathSum(int[][] grid) {
for (int i = grid.length - 1; i >= 0; i--) {
for (int j = grid[0].length - 1; j >= 0; j--) {
if (i < grid.length - 1 && j < grid[0].length - 1) {
grid[i][j] = Math.min(grid[i + 1][j], grid[i][j + 1]) + grid[i][j];
} else if (i == grid.length - 1 && j < grid[0].length - 1) {
grid[i][j] = grid[i][j + 1] + grid[i][j];
} else if (i < grid.length - 1 && j == grid[0].length - 1) {
grid[i][j] = grid[i + 1][j] + grid[i][j];
}
}
}
return grid[0][0];
}
}