2020.7.23 力扣每日
1 class Solution { 2 public int minPathSum(int[][] grid) { 3 int row = grid.length; 4 int col = grid[0].length; 5 int[][] dp = new int[row][col]; 6 if (row == 0 || col == 0) //空数组 7 return 0; 8 dp[0][0] = grid[0][0]; 9 for (int i = 1; i < row; i++){ //设置第1列 10 dp[i][0] = dp[i - 1][0] + grid[i][0]; 11 } 12 for (int i = 1; i < col; i++){ //设置第1行 13 dp[0][i] = dp[0][i - 1] + grid[0][i]; 14 } 15 for (int i = 1; i < row; i++){ //遍历其余位置 16 for (int j = 1; j < col; j++){ 17 int up = dp[i - 1][j] + grid[i][j]; //从上移动到当前节点 18 int left = dp[i][j - 1] + grid[i][j]; //从左移动到当前节点 19 dp[i][j] = (up < left) ? up : left; //比较去最小值 20 } 21 } 22 return dp[row - 1][col - 1]; 23 } 24 }
解题思路:
这是一道经典的动态规划题目,由于移动方向为向右与向下,说明除了第一行第一列以外,其余点的对应最小路径和,只可能是从左移动至当前位置的路径和,或是从上方移动至当前位置的路径和。我们使用二维数组dp[row][col]存储所有点的对应路径和情况。
用up,left分别存储从上,与从左的两种情况,显然动态方程为dp[i][j] = (up < left) ? up : left;
注意点:
- 对于第一列,由于其没有左方元素,所以dp值为dp[i - 1][0] + grid[i][0]
- 对于第一行,由于其没有上方元素,所以dp值为dp[0][i - 1] + grid[0][i]
空间复杂度:O(M*N),M,N为矩阵的行数与列数
时间复杂度:O(M*N)
优化:
我们发现,事实上每一行的元素的dp值,实际上只使用到了上一行的dp值与该行的前一位dp值,所以可以通过设置一维数组dp[col],优化空间复杂度。
1 class Solution { 2 public int minPathSum(int[][] grid) { 3 int row = grid.length; 4 int col = grid[0].length; 5 int[] dp = new int[col]; 6 if (col == 0) //数组为空 7 return 0; 8 dp[0] = grid[0][0];
9 for (int i = 1; i < col; i++){ //设置第一行的dp 10 dp[i] = dp[i - 1] + grid[0][i]; 11 } 12 for (int i = 1; i < row; i++){ //遍历其余行 13 dp[0] = dp[0] + grid[i][0]; //每行开始为上一行+当前值 14 for (int j = 1; j < col; j++){ 15 int left = dp[j - 1] + grid[i][j]; //从左处移动到当前节点 16 int up = dp[j] + grid[i][j]; //从上方移动到当前节点上 17 dp[j] = (left < up) ? left : up; //取最小值 18 } 19 } 20 return dp[col - 1]; 21 } 22 }
解题思路:
同样,先设置第一行的dp值。在通过循环遍历,对于每个节点来说,还未计算时,对应的dp[j]存储的是上方节点的情况,而dp[j -1]则对应的是左方节点的情况,所以我们只需改变先前算法,使left = dp[j -1] + grid[i][j], up = dp[j] + grid[i][j]。最后修改dp[j]为两者中的最小值即可。
注意点:
同样,第一行与第一列需特殊处理。
- 第一行,为dp[i - 1] + grid[0][i]
- 第一列,为dp[i] + grid[i][0]
空间复杂度:O(N),N为矩阵的列数
时间复杂度:O(M*N)
题后总结:
优:对于经典的动态规划熟练掌握,能迅速的解出,并进行一定的优化
差:代码部分仍可精简,如使用Math.min