【Leetcode】120.三角形最短路径和

120.三角形最短路径和

题目

给定一个三角形,找出自顶向下的最小路径和。每一步只能移动到下一行中相邻的结点上。

相邻的结点 在这里指的是 下标 与 上一层结点下标 相同或者等于 上一层结点下标 + 1 的两个结点。

 

例如,给定三角形:

[
    [2],
    [3,4],
    [6,5,7],
    [4,1,8,3]
]
自顶向下的最小路径和为 11(即,2 + 3 + 5 + 1 = 11)

方法1(暴力递归)

设f(i,j)为从位置(i,j)到最后一行的最短路径和。则可以发现如下规律:
$$f(i,j) = min(f(i+1,j) , f(i+1,j+1)) + triangle[i][j]$$

根据上述规律,很容易就可以写出以下暴力递归的方法。

1
2
3
4
5
6
7
8
public int minimumTotal(List<List<Integer>> triangle) {
return help(triangle, 0, 0);
}
public int help(List<List<Integer>> triangle, int i, int j){
if(i = triangle.size())
return 0;
return Math.min(help(triangle, i + 1, j), help(triangle, i + 1, j + 1)) + triangle.get(i).get(j));
}

暴力递归的方法会产生很多重复计算,因此动态规划方法要用额外的dp数组来进行优化。

方法2(动态规划)

对暴力递归方法进行分析可以知道,用来确定返回值状态的可变参数为i和j,也即位置。

因此定义二维的dp数组。其中dp[i][j]的含义为:从(i, j)位置走到三角形最后一行的最短路径和。

状态转移方程为:
$$dp(i,j) = min(dp(i+1,j) , dp(i+1,j+1)) + triangle[i][j]$$

由三角形的最后一行不断向上进行状态转移。

1
2
3
4
5
6
7
8
9
10
11
12
13
public int minimumTotal(List<List<Integer>> triangle) {
int n = triangle.size();
int[][] dp = new int[n][n];
//先把不被依赖的位置(最后一行)对应dp数组的值填好
for(int i = 0; i < n; i++){
dp[n - 1][i] = triangle.get(n - 1).get(i);
}
for(int i = n - 2; i >= 0; i--){
for(int j = 0; j <= i; j++)
dp[i][j] = Math.min(dp[i+1][j] , dp[i+1][j+1]) + triangle.get(i).get(j);
}
return dp[0][0];
}

时间复杂度:O(n^2)
空间复杂度:O(n^2)

方法3(对空间进行优化)

对方法2的实际运行过程进行分析,可以发现。对于dp[i][j]的计算,只需要知道dp数组第i+1行的情况即可,因此无需将整个dp数组的N行全部保存,只保存第i+1行这一行就可以了。

1
2
3
4
5
6
7
8
9
10
11
12
public int minimumTotal(List<List<Integer>> triangle) {
int n = triangle.size();
int[] dp = new int[n];
for(int j = 0; j < n; j++)
dp[j] = triangle.get(n - 1).get(j);
for (int i = n - 2; i >= 0; i--) {
for (int j = 0; j <= i; j++) {
dp[j] = Math.min(dp[j], dp[j + 1]) + triangle.get(i).get(j);
}
}
return dp[0];
}

时间复杂度:O(n^2)
空间复杂度:O(n)