Merge pull request #97 from peteryuhang/master

Solved 0120
This commit is contained in:
程序员吴师兄 2020-05-26 11:13:48 +08:00 committed by GitHub
commit 62bcddcb65
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 129 additions and 0 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 MiB

Binary file not shown.

View File

@ -0,0 +1,129 @@
# LeetCode 120 号问题三角形最小路径和
> 本文首发于公众号图解面试算法 [图解 LeetCode](<https://github.com/MisterBooo/LeetCodeAnimation>) 系列文章之一
>
> 同步博客https://www.algomooc.com
题目来源于 LeetCode 上第 120 号问题三角形最小路径和题目难度为 Medium目前通过率为 64.7%
<br>
### 题目描述
给定一个三角形找出自顶向下的最小路径和每一步只能移动到下一行中相邻的结点上
相邻的结点 在这里指的是 下标 上一层结点下标 相同或者等于 上一层结点下标 + 1 的两个结点
**示例 1:**
```
[
[2],
[3,4],
[6,5,7],
[4,1,8,3]
]
```
自顶向下的最小路径和为 112 + 3 + 5 + 1 = 11
**说明**
如果你可以只使用 O(n) 的额外空间n 为三角形的总行数来解决这个问题那么你的算法会很加分
<br>
### 题目解析
给定一个三角形数组需要求出从上到下的最小路径和再确定这道题目可以用动态规划来解后可以按照四个步骤来分析
* 问题拆解
这里的总问题是求出最小的路径和路径是这里的分析重点路径是由一个个元素组成的`[i][j]` 位置的元素经过这个元素的路径肯定也会经过 `[i - 1][j]` 或者 `[i - 1][j - 1]`因此经过一个元素的路径和可以通过这个元素上面的一个或者两个元素的路径和得到
* 状态定义
状态的定义一般会和问题需要求解的答案联系在一起这里其实有两种方式一种是考虑路径从上到下另外一种是考虑路径从下到上因为元素的值是不变的所以路径的方向不同也不会影响最后求得的路径和如果是从上到下你会发现在考虑下面元素的时候起始元素的路径只会从 [i - 1][j] 获得每行当中的最后一个元素的路径只会从 [i - 1][j - 1] 获得中间二者都可这样不太好实现因此这里考虑从下到上的方式状态的定义就变成了 **最后一行元素到当前元素的最小路径和**对于 [0][0] 这个元素来说最后状态表示的就是我们的最终答案
* 递推方程
状态定义 中我们已经定义好了状态递推方程就出来了
```
dp[i][j] = Math.min(dp[i + 1][j], dp[i + 1][j + 1]) + triangle[i][j]
```
* 实现
这里初始化时我们需要将最后一行的元素填入状态数组中然后就是按照前面分析的策略从下到上计算即可
这里有一个小小的空间上面的优化就是每次我们更新状态(dp)数组都是基于之前的结果我们并不需要知道之前的之前的结果平行的状态之间也没有相互影响因此只用开一维数组即可
<br>
### 代码实现空间优化前
```java
public int minimumTotal(List<List<Integer>> triangle) {
int n = triangle.size();
int[][] dp = new int[n][n];
List<Integer> lastRow = triangle.get(n - 1);
for (int i = 0; i < n; ++i) {
dp[n - 1][i] = lastRow.get(i);
}
for (int i = n - 2; i >= 0; --i) {
List<Integer> row = triangle.get(i);
for (int j = 0; j < i + 1; ++j) {
dp[i][j] = Math.min(dp[i + 1][j], dp[i + 1][j + 1]) + row.get(j);
}
}
return dp[0][0];
}
```
<br>
### 代码实现空间优化后
```java
public int minimumTotal(List<List<Integer>> triangle) {
int n = triangle.size();
int[] dp = new int[n];
List<Integer> lastRow = triangle.get(n - 1);
for (int i = 0; i < n; ++i) {
dp[i] = lastRow.get(i);
}
for (int i = n - 2; i >= 0; --i) {
List<Integer> row = triangle.get(i);
for (int j = 0; j < i + 1; ++j) { // i + 1 == row.size()
dp[j] = Math.min(dp[j], dp[j + 1]) + row.get(j);
}
}
return dp[0];
}
```
<br>
### 动画描述
![](../Animation/120.gif)
<br>
### 复杂度分析
时空复杂度从代码中都清晰可见我们必须遍历三角形中的每个元素时间复杂度就是 `O(1 + 2 + ... + n)`也就是 `O(n^2)`空间复杂度经过优化后是 `O(n)`
![](../../Pictures/qrcode.jpg)