三角形最小路径和
二 题目
给定一个三角形,找出自顶向下的最小路径和。
每一步只能移动到下一行中相邻的结点上。
相邻的结点 在这里指的是 下标 与 上一层结点下标 相同
或者等于 上一层结点下标 + 1 的两个结点。
例如,给定三角形:
[
[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
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
/**
* @param {number[][]} triangle
* @return {number}
*/
var minimumTotal = function(triangle) {
};
根据上面的已知函数,小伙伴们可以先尝试破解本题,确定了自己的答案后再看下面代码。
三 解题思路
首先,我们抛弃动态规划等优秀解答,写一个简单满足面试官要求的题解。
我们的思路很清晰:
拿其中一个例子说明,它的数据 routeList
的走向将如下:
[
[-1],
[3,2],
[-3,1,-1],
]
------
[ -1 ]
------
[ 2, 1 ]
------
[ -1, 2, 0 ]
- 由顶往下靠,将每一步的最优解给提取出来
- 第一步的时候,组合
[-1], []
,得到[routeList, minSum]
为[[-1], -1]
。 - 第二步的时候,组合
[3, 2], [-1]
,得到[routeList, minSum]
为[[2, 1], 1]
。 - 第三步的时候,组合
[-3, 1, -1], [2, 1]
,得到[routeList, minSum]
为[[-1, 2, 0], -1]
。
/**
* @name combine
* @description 简单版本 两两组合
* @param {any} arr1 数组 1
* @param {any} arr2 数组 2
*/
const combine = (arr1, arr2) => {
const combineList = []; // 组合后的结果集
let minSum = Number.MAX_SAFE_INTEGER; // 最小值
// 如果是第一次传递
if (!arr1.length) {
minSum = arr2[0];
return [arr2, minSum];
}
// 如果两个数组不为空
for (let i = 0; i < arr2.length; i++) {
let tempSum = Number.MAX_SAFE_INTEGER;
for (let j = 0; j < arr1.length; j++) {
// 很奇怪的是,题目标明了:
// 每一步只能移动到下一行中相邻的结点上。
// 相邻的结点 在这里指的是 下标 与 上一层结点下标 相同
// 或者等于 上一层结点下标 + 1 的两个结点。
// 然后我写了个 Math.abs(i - j) < 2,差点想得脑袋裂开
if (i - j === 1 || i - j === 0) {
tempSum = Math.min(arr2[i] + arr1[j], tempSum);
}
}
minSum = Math.min(tempSum, minSum);
combineList.push(tempSum);
}
return [combineList, minSum];
};
/**
* @param {number[][]} triangle
* @return {number}
*/
const minimumTotal = (triangle) => {
// 思路:将所有路径找出来
// 然后比较所有路径的总和,取最小的一个
let routeList = [];
let minSum = 0;
for (let i = 0; i < triangle.length; i++) {
[routeList, minSum] = combine(routeList, triangle[i]);
}
return minSum;
};
// 正常内容示例
console.log(minimumTotal(
[
[2],
[3,4],
[6,5,7],
[4,1,8,3]
]
)); // 11
// 只有一个示例
console.log(minimumTotal(
[
[-10],
]
)); // -10
// Math.abs(i - j) < 2 错误示例
console.log(minimumTotal(
[
[-1],
[3,2],
[-3,1,-1],
]
)); // -1
满足基本需求,搞定。
这时候,如果你对上面的思路清楚了,那么我们就可以想象下:
- 上面我们是对每次的数组进行了累加。
- 最终我们依靠
minSum
获取最终结果。
那么,我们能不能减少变量,优化思路,从而获取更优解?
动态规划
const minimumTotal = (triangle) => {
for (let i = triangle.length - 2; i >= 0; i--) {
for (let j = 0; j < triangle[i].length; j++) {
triangle[i][j] = Math.min(
triangle[i + 1][j],
triangle[i + 1][j + 1],
) + triangle[i][j];
console.log('------');
console.log(triangle);
}
}
return triangle[0][0];
};
先看一下打印情况:
[
[-1],
[3,2],
[-3,1,-1],
]
------
[ [ -1 ], [ 0, 2 ], [ -3, 1, -1 ] ]
------
[ [ -1 ], [ 0, 1 ], [ -3, 1, -1 ] ]
------
[ [ -1 ], [ 0, 1 ], [ -3, 1, -1 ] ]
可以看到,我们自底向上开始,每层进行累加,将最终结果累加到 triangle[0][0]
中。
这样,我们就逐步完善了我们的代码。
四 统计分析
本题不需要统计分析。
五 套路分析
本题暂未发现任何套路,如果有但是 jsliang 后面发现了的话,会在 GitHub 进行补充。
jsliang 的文档库 由 梁峻荣 采用 知识共享 署名-非商业性使用-相同方式共享 4.0 国际 许可协议进行许可。
基于https://github.com/LiangJunrong/document-library上的作品创作。
本许可协议授权之外的使用权限可以从 https://creativecommons.org/licenses/by-nc-sa/2.5/cn/ 处获得。