二叉树的最大路径和

一句话说明

这题必须把“返回给父节点的贡献值”和“当前节点作为拐点时的完整路径值”拆开看,否则一定会写乱。

这题为什么容易卡住

很多人会误以为它求的是:

  • 根到叶最大路径和
  • 或者某条一直向下的单链最大和

都不对。
这题的路径可以:

  • 从任意节点开始
  • 到任意节点结束
  • 但必须沿父子边连续走

所以路径不一定经过根,也不一定只能单向往下。

这题最关键的两个量

向上贡献值

gain(node) 表示:

如果这条路径还要继续接到父节点,
当前节点最多能提供多少价值?

因为往上接时不能分叉,所以只能选左边或右边的一条链。

全局答案

answer 表示整棵树里见过的最大路径和。
它允许当前节点把左右两边都接上,把自己当拐点。

Go 代码:标准模板

func MaxPathSum(root *TreeNode) int {
    answer := math.MinInt
 
    var gain func(node *TreeNode) int
    gain = func(node *TreeNode) int {
        if node == nil {
            return 0
        }
 
        leftGain := max(gain(node.Left), 0)
        rightGain := max(gain(node.Right), 0)
 
        currentPath := node.Val + leftGain + rightGain
        answer = max(answer, currentPath)
 
        return node.Val + max(leftGain, rightGain)
    }
 
    gain(root)
    return answer
}
 
func max(a, b int) int {
    if a > b {
        return a
    }
    return b
}

为什么负贡献直接按 0 处理

如果某个子树贡献是负数,接上它只会让路径更差。
而题目允许路径从任意节点开始,所以没必要硬把负数子树带上。

这就是:

leftGain := max(gain(node.Left), 0)
rightGain := max(gain(node.Right), 0)

背后的真正原因。

一个例子走通

       -10
       /  \
      9   20
         /  \
        15   7

在节点 20:

  • 左贡献 15
  • 右贡献 7
  • 当前完整路径 20 + 15 + 7 = 42

这会刷新全局答案。
但返回给父节点时,只能返回:

20 + max(15, 7) = 35

因为往上只能接一条链。

为什么这是后序思路

因为当前节点必须先知道:

  • 左子树给它多少贡献
  • 右子树给它多少贡献

然后才能决定:

  • 当前节点作为拐点时答案是多少
  • 向父节点汇报多少

这就是非常典型的后序递归。

易错点

最大路径和最容易错的地方

  • 返回给父节点时只能选左右一边,不能把两边都带上去。
  • 全局答案和递归返回值不是同一个东西。
  • 所有节点都为负数时,答案是其中最大的那个负数,所以全局初值不能写成 0。

复杂度

方法时间复杂度空间复杂度
递归O(n)O(h)

相关主题


返回:树算法 | 算法学习导航