leetcode-120三角形最小路径和

286 阅读3分钟

题目描述

给定一个三角形,找出自顶向下的最小路径和。每一步只能移动到下一行中相邻的结点上。

相邻的结点 在这里指的是 下标上一层结点下标 相同或者等于 上一层结点下标 + 1 的两个结点。

例如,给定三角形:
[
     [2],
    [3,4],
   [6,5,7],
  [4,1,8,3]
]

自顶向下的最小路径和为11(即,2 + 3 + 5 + 1 = 11)。

说明

如果你可以只使用 O(n) 的额外空间(n 为三角形的总行数)来解决这个问题,那么你的算法会很加分。

题解一

这道题目是一个典型的动态规划的问题。 假设矩阵triangle[m][n]表示三角形的顶点。f[i][j]为从最上层顶点triangle[0][0]triangle[i][j]的最小路径和,那么状态转移方程为:

if(j == 0) {
    f[i][0] = f[i-1][0]+triangle[i][0]
}else if(j == i){
    f[i][j] = f[i-1][j-1]+triangle[i][i]
}else {
    f[i][j] = min(f[i-1][j], f[i-1][j-1])+triangle[i][j]
}
很显然f[0][0] = triangle[0][0];

代码如下:

/// 三角形最小路径
/// @param triangle 表示三角形的二维矩阵
/// @param triangleSize 矩阵的行数
/// @param triangleColSize 好像没有用到
int minimumTotal(int** triangle, int triangleSize, int* triangleColSize){
    int ans[triangleSize][triangleSize];
    memset(ans, 0, sizeof(ans));
    ans[0][0] = triangle[0][0];
    for(int i = 1; i < triangleSize; i++) {
        ans[i][0] = ans[i-1][0]+triangle[i][0];
        for(int j = 1; j < i; j++) {
            ans[i][j] = fmin(ans[i-1][j], ans[i-1][j-1])+triangle[i][j];
        }
        ans[i][i] = ans[i-1][i-1]+triangle[i][i];
    }
    int result = ans[triangleSize-1][0];
    for(int i = 1; i < triangleSize; i++) {
        result = fmin(result, ans[triangleSize-1][i]);
    }
    return result;
}

时间复杂度O(n²),空间复杂度O(n²)

题解二

上一个解法中求出了从顶点到任意一点的最小路径,其实题目要求的只是从顶点到最小层的距离,对上一题的解法可以进行优化,减少空间复杂度为O(n)

状态转移方程式并没有发生变化,变化的是我们只用一个一维数组来记录每一次循环的结果,在下一次循环过程中更新这个一维数组。需要注意的是,数组的更新需要从每一层逆序更新,否则的话会出现数据错乱的情况。

[
     [2],
    [3,4],
   [6,5,7],
  [4,1,8,3]
]
在本例当中,
第一层:f[0] = 2;
第二层:
    1. 正序计算:
                f[0] = f[0]+triangle[1][0] = 2+3 = 5;
                f[1] = f[0]+triangle[1][1] = 5+4 = 9;出错,因为f[0]已经被更新了。
    2. 逆序计算:
                f[1] = f[0]+triangle[1][1] = 2+4 = 6;
                f[0] = f[0]+triangle[1][0] = 2+3 = 5;正确
第三层:
    逆序计算:
                f[2] = f[1]+triangle[2][2] = 6+7 = 13;
                f[1] = min(f[1], f[0])+triangle[2][1] = 5+5 = 10;
                f[0] = f[0]+triangle[2][0] = 5+6 = 11;
....

代码如下:

int minimumTotal(int** triangle, int triangleSize, int* triangleColSize){
    int f[triangleSize];
    memset(f, 0, sizeof(f));
    f[0] = triangle[0][0];
    for(int i = 1; i < triangleSize; i++) {
        f[i] = f[i-1] + triangle[i][i];
        for(int j = i-1; j > 0; j--) {
            f[j] = min(f[j-1], f[j])+triangle[i][j];
        }
        f[0] = f[0]+triangle[i][0];
    }
    int res = f[0];
    for(int i = 1; i < triangleSize; i++) {
        res = min(res, f[i]);
    }
    return res;
}

时间复杂度O(n²),空间复杂度O(n)

题解三

我们也可以利用从下玩上的顺序进行求解,即从三角形的最后一层各个顶点出发,最终达到三角形的顶点,求出最短路径。这种接法和题解二刚好相反,必须要正序遍历,不能进行逆序遍历。

int minimumTotal(int** triangle, int triangleSize, int* triangleColSize) {
    int *ans = triangle[triangleSize-1];
    for(int i = triangleSize-2; i >= 0; i--) {
        int *curCol = triangle[i];
        for(int j = 0; j < i+1; j++) {
            ans[j] = min(ans[j+1], ans[j]) + curCol[j];
        }
    }
    return ans[0];
}

时间复杂度O(n²),空间复杂度O(n)