LeetCode刷题——120. 三角形最小路径和

题目

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

例如,给定三角形:

[
     [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

思路

题目要求每一步只能移动到下一行中相邻的节点上。比如3只能移动到6或5;4只能移动到5或7。

我们先来思考下能否递归的解决,它内部是否有递归的结构。

我们自顶向下的思考问题,先考虑最上面的那一层(第0层,用(0,0)表示),然后向下移动一步,只能是3(1,0)或4(1,1)。假设我们已经知道3或4哪个是最小的。
那么直接用当前路径(2)加上经过3或4中路径最小的即可。

在这里插入图片描述
这里用(level,index)表示这颗树上的某个节点,比如顶层(第0层)就是(0,0);第1层就是(1,0)和(1,1)。

我们这里简单的画一下递归树,可以看到只画了三层就已经存在重叠子问题了。我们先用递归算法写出来,然后用记忆化搜索来优化,最后改成动态规划。

扫描二维码关注公众号,回复: 11203142 查看本文章

代码

递归

class Solution(object):
    def minimum(self,triangle,i,level,last_level):
        if level == last_level:
            return triangle[level][i]#如果是最底层,返回本身
        #否则返回当前节点值加下层中最小值
        return triangle[level][i] + min(
        self.minimum(triangle,i,level+1,last_level),
        self.minimum(triangle,i+1,level+1,last_level))

    def minimumTotal(self, triangle):
        """
        :type triangle: List[List[int]]
        :rtype: int
        """
        last_level = len(triangle) - 1 # 保存了最后一层的值
        return self.minimum(triangle,0,0,last_level) #从(0,0)开始
        

在这里插入图片描述
整个递归思路是没问题的,虽然耗时较长导致超时,但是我们可以基于此思路改写成记忆化搜索的方式(参阅LeetCode刷题之动态规划思想)。

记忆化搜索



class Solution(object):
    def minimum(self,triangle,i,level,last_level,dp):
        if level == last_level:
            return triangle[level][i]
     
     	# 通过dp保存了(level,i)的计算值,防止重复计算
        if (level,i) not in dp:
            dp[(level,i)] =  triangle[level][i] + min(
        self.minimum(triangle,i,level+1,last_level,dp),
        self.minimum(triangle,i+1,level+1,last_level,dp)) #否则返回当前节点值加下层

        return dp[(level,i)]

    def minimumTotal(self, triangle):
        """
        :type triangle: List[List[int]]
        :rtype: int
        """
        dp = {}
        last_level = len(triangle) - 1
        return self.minimum(triangle,0,0,last_level,dp)

改写的方式就比较直观,这里把dp作为参数进行递归传递。

在这里插入图片描述
很好,改成记忆化搜索的方式就可以通过了。
但是我们的最终目标是改成动态规划的形式。

动态规划

class Solution(object):
    def minimumTotal(self, triangle):
        """
        :type triangle: List[List[int]]
        :rtype: int
        """        
        last_level = len(triangle) - 1
        for level in range(last_level-1,-1,-1):#由底向上,从倒数第2层到第0层
            for i in range(0,level+1):#归纳法,第level层,最多有level+1个节点
                triangle[level][i] = triangle[level][i] + min(triangle[level+1][i],triangle[level+1][i+1])

        return triangle[0][0]

在这里插入图片描述
这里我们通过改写原列表,可以省去了dp这个字典结构。从倒数第二层开始更新最小路径值,直到第0层,最终输出(0,0)的值即可。

原创文章 176 获赞 286 访问量 18万+

猜你喜欢

转载自blog.csdn.net/yjw123456/article/details/105923585