比较直观的想法是,使用一个数组来保存每次计算路径的最小值,这样顺序遍历下来,最后求一遍这个数组的最小元素就是最短路径的值
public int minimumTotal(List<List<Integer>> triangle) {
if (triangle == null || triangle.size() == 0){
return 0;
}
int size = triangle.size();
List<Integer> lastList = triangle.get(size - 1);
List<Integer> firstList = triangle.get(0);
int[] array = new int[1];
array[0] = firstList.get(0);
for (int i = 1; i < size; i++){
List<Integer> list = triangle.get(i);
int[] secondArray = new int[list.size()];
for (int j = 0; j < list.size(); j++){
int temp = Integer.MAX_VALUE;
if (j < array.length){
temp = array[j] + list.get(j);
}
if (j - 1 >= 0){
int after = array[j - 1] + list.get(j);
temp = temp > after ? after : temp;
}
secondArray[j] = temp;
}
array = secondArray;
}
int min = Integer.MAX_VALUE;
for (int x = 0; x < array.length; x ++){
min = min < array[x] ? min : array[x];
}
return min;
}
初步认为是上面的算法一方面最后找最小值要重新遍历一遍,另一方面在每层遍历时都要new一个数组比较占空间
试着把最小值的计算放在循环里,也没减少耗时,说明耗时的原因不是计算最小值
换一种思路,利用一个二维数组来做每次运算的最小值,变成一个普通的动态规划问题
public int minimumTotal(List<List<Integer>> triangle) {
if (triangle == null || triangle.size() == 0){
return 0;
}
int n = triangle.size();
int[][] dp = new int[n][n];
dp[0][0] = triangle.get(0).get(0);
for (int i = 1; i < n; i++){
dp[i][0] = dp[i-1][0] + triangle.get(i).get(0);
for (int j = 1; j < i; j++){
dp[i][j] = Math.min(dp[i-1][j],dp[i-1][j-1]) + triangle.get(i).get(j);
}
dp[i][i] = dp[i-1][i-1] + triangle.get(i).get(i);
}
int min = dp[n-1][0];
for (int i = 1; i < n; i++){
min = Math.min(min,dp[n-1][i]);
}
return min;
}
空间优化:
这个和第一次的解法类似,只是用一个大小为2的二维数组来存储状态,不是每次遍历都去new一个数组
递归+记忆化算法:
Integer[][] memo;
public int minimumTotal(List<List<Integer>> triangle) {
memo = new Integer[triangle.size()][triangle.size()];
return dfs(triangle, 0, 0);
}
private int dfs(List<List<Integer>> triangle, int i, int j) {
if (i == triangle.size()) {
return 0;
}
if (memo[i][j] != null) {
return memo[i][j];
}
return memo[i][j] = Math.min(dfs(triangle, i + 1, j), dfs(triangle, i + 1, j + 1)) + triangle.get(i).get(j);
}