LeetCode - 120. Triangle

题目中给出的三角形有一个树状的结构,这就可以让我们想到使用遍历之类的算法比如说DFS。但是如果我们忍者你分析一下的话,可以发现相邻的结点总是分享同一条边,也就是说,这道题目中存在重叠子问题(overlapping subproblems)。另外,假设结点x和结点y是k的孩子,只要从底层到结点x和y的最短路径长度都能够知道,那么从底层到结点k的最短路径长度可以在O(1)时间内判断出来,这就是最优子结构(optimal subtructure)。因此,就时间复杂度考虑,动态规划或许是一个不错的解法。

我们从底层向上看,也就是Bottom-up DP,最底层结点的minPath就是它们自身的值,而上一层节点的minPath是它下方左侧结点和下方右侧节点的最小值加上自身的值,根据这个规律,我们可以写出下面的递推式:

minPath[k][i] = min(minPath[k + 1][i], minPath[k + 1][i + 1]) + triangle[k][i];

到现在我们已经可以解决这道题目了,但是我们额外需要题目给出的triangle大小的额外空间用来存储中间结果,当triangle很大的时候,这个算法可能会消耗大量的内存,那么这个算法可否进行优化呢?答案是可以的,进一步地观察我们可以发现,当第k层的所有结点的minPath都已经计算出来之后,第k + 1层所有阶段的minPath都已经不再会用到了,所以我们可以使用一个数组,通过不断地赋给它每一层结点的minPath实现递推和迭代,也就是下面的公式:

minPath[k] = min(minPath[k] + minPath[k + 1]) + triangle[k][i](这里等号右侧的minPath[k]和minPath[k + 1]是上一层的minPath,等号左侧为这一层的minPath);

注意在最底层的时候,元素的数量为n,根据上面的递推式,我们需要长度为n + 1的数组。这个算法的时间复杂度为O(n*2),空间复杂度为O(n),代码如下:

public class Solution {
    public int minimumTotal(List<List<Integer>> triangle) {
        if(triangle == null || triangle.size() == 0 || triangle.get(0).size() == 0) return 0;      // corner case
        
        int[] minLength = new int[triangle.size() + 1];
        for(int layer = triangle.size() - 1; layer >= 0; layer--){
            for(int i = 0; i < triangle.get(layer).size(); i++){
                minLength[i] = triangle.get(layer).get(i) + Math.min(minLength[i], minLength[i + 1]);
            }
        }
        
        return minLength[0];
    }
}


知识点:

1. 动态规划问题的分析思路

2. 动态规划的优化方式,首先写出非优化版本的算法,然后在使用空间的问题上通过分析问题发现是否可以迭代,如果可以迭代则进行优化


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值