【LeetCode】二叉樹最大路徑和(dfs)
阿新 • • 發佈:2022-04-06
二叉樹最大路徑和
題目連結:https://leetcode-cn.com/problems/binary-tree-maximum-path-sum/
分析:
這個題目是求二叉樹的最大路徑和,要點有兩個:
- 最大不能走回頭路:從根節點延伸的路徑,你不能走了左子樹又掉過頭來走右子樹
- 最大路徑不一定要經過根節點
思路:
對每個非葉子節點,其實都面臨這樣一個問題:求解從此節點開始的最大路徑和,所以求解從根節點開始的最大路徑和其實可以分解成很多的子問題,這些子問題在根本上其實都是一個問題,有個通用的解法,所以我們可以採取遞迴的方式,即dfs深度搜索
針對每個節點,其最大收益分三種情況:
- 只走此節點,不走其左右孩子 ,收益:root.val
- 走此節點,並走入其左子樹,收益:root.val+dfs(root.left)
- 走此節點,並走入其右子樹,收益:root.val+dfs(root.right)
每個節點的收益僅有上面三種情況,對每個節點,取這三種情況的最大值即可:
root.val+max(0, dfs(root.left), dfs(root.right))
這也是當前子樹能夠提供給外部的最大路徑和,即左右孩子只能選擇一個走
一個子樹內部最大的路徑和:左子樹的最大路徑和+右子樹的最大路徑和+根節點值,即dfs(root.left)+root.val+dfs(root.right)
上述兩種情況,分別是子樹提供給外部(其父節點)的最大路徑和,子樹自身內部的最大路徑和
對於每個子問題,我們遞迴時都需要返回子樹提供給外部的最大路徑和
而對於子樹自身內部最大的路徑和,每次都有比較記錄一下,取最大值
var result int // 記錄最大路徑和 // dfs 求從root開始的最大路徑和 func dfs(root *TreeNode) int{ // 遍歷到nil節點,收益為0 if root==nil{ return 0 } left:=dfs(root.Left) // 左子樹內部提供的最大路徑和 right:=dfs(root.Right) // 右子樹內部提供的最大路徑和 innerMaxSum:=left+root.Val+right // 當前子樹內部的最大路徑和 result=max(result,innerMaxSum) // 挑戰一下最大路徑 outputMaxSum:=root.Val+max(0,max(left,right)) // 當前子樹對外提供的最大路徑和 // 如果子樹對外提供的最大路徑和為負數,則返回0,因為負數子樹對最大路徑和毫無貢獻 if outputMaxSum<0{ return 0 } return outputMaxSum // 返回當前子樹對外提供的最大路徑和,這是每個子問題的解 } func max(a,b int) int{ if a>b{ return a } return b } func maxPathSum(root *TreeNode) int { result=math.MinInt64 dfs(root) return result }
耗時分析:
時間複雜度:O(N),每個節點都需要遍歷
空間複雜度:O(H),也就是遞迴的深度,跟樹的形狀有關
總結:
- 學會把大問題拆解成很多子問題,每個子問題其實都是相似的,子問題有解決方案了,綜合一下大問題也就解決了
- 通過求出每個子樹對外提供的最大路徑和,從遞迴樹底部向上,不斷求出每個子樹內部的最大路徑和(記錄最大值),後者是求解的目標,後者的求解需要前者
- 每個子樹內部的最大路徑和,都挑戰一下最大路徑和,這樣遞迴結束時,最大路徑和也就出來了