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)。

我們這裏簡單的畫一下遞歸樹,可以看到只畫了三層就已經存在重疊子問題了。我們先用遞歸算法寫出來,然後用記憶化搜索來優化,最後改成動態規劃。

代碼

遞歸

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)的值即可。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章