题目描述
给定一个三角形,找出自顶向下的最小路径和。每一步只能移动到下一行中相邻的结点上。
例如,给定三角形:
[
[2],
[3,4],
[6,5,7],
[4,1,8,3]
]
自顶向下的最小路径和为 11(即,2 + 3 + 5 + 1 = 11)。
说明:
如果你可以只使用 O(n) 的额外空间(n 为三角形的总行数)来解决这个问题,那么你的算法会很加分。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/triangle
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
我的思路
从上到下依次计算每一行每个点的最小路径总和,并把该最小值覆盖到该点。计算结束后,最后一行的最小值即为最终的最小路径和。
以上面的例子为例:
首先看第二行,第一个点3
,从顶点2
到当前点,只有一种可能2 + 3 = 5
,把3
修改为5
,同理2
到第二行第二个点4
也只有一种路径,2 + 4 = 6
,把4
修改为6
,得到如下结果:
[
[2],
[5,6],
[6,5,7],
[4,1,8,3]
]
然后再看顶点到第三行的3个点最小路径分别是多少,因为上一行的计算结果已经包含了前面的最短路径信息,所以原来的2
到第三行6
的最短路径和,就等价于现在第二行的5
到第三行6
的最短路径和(第二行的6
到不了第三行的6
)。顶点到第三行5
等价于第二行5
或6
到第三行5
的最小路径和。······
下面直接给出每更新一行的结果:
第三行更新:
[
[2],
[5, 6],
[11,10,13],
[4, 1, 8, 3]
]
第四行更新:
[
[2],
[5, 6],
[11,10,13],
[15,11,18,16]
]
然后取出最后一行的最小值11
,就是总的最小路径和。
我的程序(Java)
class Solution {
public int minimumTotal(List<List<Integer>> triangle) {
if(triangle.size() == 0) return 0;
if(triangle.size() == 1) return triangle.get(0).get(0);
int minTotal = Integer.MAX_VALUE;
for(int i = 1; i < triangle.size(); i++){
int rowSize = triangle.get(i).size(); // 当前行的元素个数
for(int j = 0; j < rowSize; j++){
int last = 0;
if(j == 0){
last = triangle.get(i - 1).get(j); // 获取上一层同一索引的相邻值
}else if(j > 0 && j < rowSize - 1){ // 遍历的为非首尾元素
// 获取上一行相邻值的最小值
last = Math.min(triangle.get(i - 1).get(j), triangle.get(i - 1).get(j - 1));
}else if(j == rowSize - 1){ // 当前行最后一个元素
// 获取上一行的相邻值(当前位置索引-1),左上角的值
last = triangle.get(i - 1).get(j - 1);
}
int newValue = triangle.get(i).get(j) + last; // 获取当前位置元素值
if(i == triangle.size() - 1){ // 最后一行
if(newValue < minTotal){
minTotal = newValue;
}
}else{
triangle.get(i).set(j, newValue);
}
}
}
return minTotal;
}
}
其他方法
其他方法:自底向上的动态规划法
下图来自LeetCode评论区的精选评论第二条,我就直接截图过来了。
下面是提交记录里面copy过来的(自行添加了一点注释):
class Solution {
int row;
Integer[][] memo;
public int minimumTotal(List<List<Integer>> triangle) {
row=triangle.size();
memo=new Integer[row][row]; // 用于保存每一行的计算结果,一开始没有初始化,全部为null
return helper(0,0,triangle); // 递归调用
}
public int helper(int level,int column,List<List<Integer>> triangle) {
if(memo[level][column]!=null) {
return memo[level][column];
}
if(level==row-1) { // 拷贝最后一行的值,并返回
return memo[level][column]=triangle.get(level).get(column);
}
int left=helper(level+1,column,triangle); // 下一行的左边数值
int right=helper(level+1,column+1,triangle); // 下一行的右边数值
// 计算当前位置自底向上的最小值
return memo[level][column]=Math.min(left, right)+triangle.get(level).get(column);
}
}
用到了自底向上的动态规划和递归。
如有不当之处,欢迎读者批评指正!