题目描述:
给定一个三角形 triangle ,找出自顶向下的最小路径和。
每一步只能移动到下一行中相邻的结点上。相邻的结点 在这里指的是 下标 与 上一层结点下标 相同或者等于 上一层结点下标 + 1 的两个结点。也就是说,如果正位于当前行的下标 i ,那么下一步可以移动到下一行的下标 i 或 i + 1 。
示例 1:
输入:triangle = [[2],[3,4],[6,5,7],[4,1,8,3]]
输出:11
解释:如下面简图所示:
2
3 4
6 5 7
4 1 8 3
自顶向下的最小路径和为 11(即,2 + 3 + 5 + 1 = 11)。
示例 2:
输入:triangle = [[-10]]
输出:-10
提示:
1 <= triangle.length <= 200
triangle[0].length == 1
triangle[i].length == triangle[i - 1].length + 1
-104 <= triangle[i][j] <= 104
来源:力扣(LeetCode) 链接:https://leetcode-cn.com/problems/triangle
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
Note:
int minimumTotal(int** triangle, int triangleSize, int* triangleColSize)
对于力扣给的函数定义,简单说明一下:
- triangle参数:是指存放二维数组每行的指针的数组 的指针,有点绕口,其实就是用一个指针去指向存放指针的数组,这主要是因为c语言中列数不等长的二维数组,可以通过先建立每行的数组,然后将每行数组的指针存放到一个新的数组里面,最后用一个指针来指向这个数组。
- triangleSize:是指的二维数组的行数,也就是存放一维数组的指针的数组的长度。
- triangleColSize:是指的存放每一行的列数的数组的指针。
- 只有传入这三个参数才可以去操作这个二维数组。
用下面的例子简单说明一下:
int a1[1] = {0}; //第一行
int a2[2] = {0}; //第二行
int a3[3] = {0}; //第三行
int *a[3] = {a1, a2, a3}; //指针数组
int **p; //指向指针的指针
p = a; //令该指针指向盛放二维数组每行的起始地址。
后面只需要将p、a[3]的大小、a[3]中的每个一维数组的长度放到一个数组里形成的数组,传到函数里面就可以了。
而至于访问二维数组中的元素通过p指针就可以了,如p[0][0]就是访问二维数组中的第一行第一列的元素。
因为力扣刷题是函数式编程的方式,相当于给你函数的定义,然后去实现这个函数,而函数中为了跑一些测试用例的方便,所以以指针的形式作为形参,这样直接修改原数据而指针不用改变。
回归正题,分析下这个题目:
分析:
画格子,写出递推方程,根据递推的顺序,来遍历格子,然后写代码。
这道题目比较经典,递推公式也比较容易写出来:
f
(
i
,
j
)
=
m
i
n
{
f
[
i
+
1
]
[
j
]
,
f
[
i
+
1
]
[
j
+
1
]
}
+
a
[
i
]
[
j
]
f(i, j) =min\{ f[i+1][j],f[i+1][j+1] \} + a[i][j]
f(i,j)=min{f[i+1][j],f[i+1][j+1]}+a[i][j]
上述式子采用自底向上的递推:
f
(
i
,
j
)
f(i,j)
f(i,j)表示从底端到
f
(
i
,
j
)
f(i,j)
f(i,j)处的最短路径
代码实现如下:
int minimumTotal(int** triangle, int triangleSize, int* triangleColSize){
for(int i=triangleSize-2;i>=0;--i){
for(int j=0;j<=i;++j){
triangle[i][j]=fmin(triangle[i+1][j],triangle[i+1][j+1])+triangle[i][j];
}
}
return triangle[0][0];
}
另一种方式:像上面这种方式,直接将结果存在了原来的triangle中了,如何才能不改动原来的二维数组,当然也可以直接建立一个新的二维数组,如果还想进一步节省空间的话,可以通过建立一个一维数组来保存当前状态,具体如下:
int minimumTotal(int** triangle, int triangleSize, int* triangleColSize){
//由于不需要保存所有的状态,而且当前状态只和上一状态有关
//因此只需要一个一维数组
int *res = (int*)malloc(sizeof(int) * triangleColSize[triangleSize-1]);
//结果最后一行就是原来的最后一行
memcpy(res, triangle[triangleSize - 1],
sizeof(int) * triangleColSize[triangleSize-1]);
//从最后第2行开始递推
for (int i = triangleSize - 2; i >= 0 ; i-- ){
for ( int j = 0 ; j < triangleColSize[i]; j++){
//第(i,j)元素的值为(i+1,j), (i+1,j+1)中的最小值
res[j] = fmin(res[j], res[j+1]) + triangle[i][j];
}
}
return res[0];
}
本地测试完整代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
int minimumTotal(int** triangle, int triangleSize, int* triangleColSize){
int *res = (int*)malloc(sizeof(int) * triangleColSize[triangleSize - 1]);
memcpy(res, triangle[triangleSize - 1], sizeof(int) * triangleColSize[triangleSize-1]);
for(int i = triangleSize - 2; i >= 0; --i)
{
for(int j = 0; j < triangleColSize[i]; j++)
{
res[j] = fmin(res[j], res[j+1]) + triangle[i][j];
}
}
return res[0];
}
int main() {
//二维数组每行的元素
int a1[1] = {2};
int a2[2] = {3, 4};
int a3[3] = {6, 5, 7};
//存放二维数组每行的起始地址的指针数组
int *a[3] = {a1, a2, a3};
//设置一个指针,指向存放各行指针的数组
int **p;
p = a;
int pSize = 3; //二维数组的行数
int columnSize[3] = {1, 2, 3}; //二维数组每行的列数
int res = minimumTotal(p, pSize, columnSize);
printf("%d", res);
}
附:python3版本代码:
```python
class Solution:
def minimumTotal(self, triangle: List[List[int]]) -> int:
i = len(triangle) - 2
for i in range(i, -1, -1):
for j in range(i, -1, -1):
triangle[i][j] += min(triangle[i+1][j], triangle[i+1][j+1])
return triangle[0][0]
class Solution:
def minimumTotal(self, triangle: List[List[int]]) -> int:
i = len(triangle) - 1
res = [x for x in triangle[i]]
for i in range(len(triangle)-2, -1, -1):
for j in range(len(triangle[i])):
res[j] = min(res[j], res[j+1]) + triangle[i][j]
i -= 1
return res[0]