1. 程式人生 > 其它 >【LeetCode】二叉樹最大路徑和(dfs)

【LeetCode】二叉樹最大路徑和(dfs)

二叉樹最大路徑和

題目連結: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),也就是遞迴的深度,跟樹的形狀有關

總結:

  • 學會把大問題拆解成很多子問題,每個子問題其實都是相似的,子問題有解決方案了,綜合一下大問題也就解決了
  • 通過求出每個子樹對外提供的最大路徑和,從遞迴樹底部向上,不斷求出每個子樹內部的最大路徑和(記錄最大值),後者是求解的目標,後者的求解需要前者
  • 每個子樹內部的最大路徑和,都挑戰一下最大路徑和,這樣遞迴結束時,最大路徑和也就出來了