题解 | #二叉树的最大路径和#

二叉树的最大路径和

http://www.nowcoder.com/practice/da785ea0f64b442488c125b441a4ba4a

算法思想一:递归

解题思路:

首先,考虑实现一个简化的函数 maxGain(node),该函数计算二叉树中的一个节点的最大贡献值,具体而言,就是在以该节点为根节点的子树中寻找以该节点为起点的一条路径,使得该路径上的节点值之和最大。
具体而言,该函数的计算如下。
空节点的最大贡献值等于 0。
非空节点的最大贡献值等于节点值与其子节点中的最大贡献值之和(对于叶节点而言,最大贡献值等于节点值)

例如二叉树:root = [-10,9,20,null,null,15,7]
叶节点 9、15、7 的最大贡献值分别为 9、15、7。
得到叶节点的最大贡献值之后,再计算非叶节点的最大贡献值。节点 20 的最大贡献值等于 20+max(15,7)=35,节点 -10 的最大贡献值等于 −10+max(9,35)=25。
上述计算过程是递归的过程,因此,对根节点调用函数 maxGain,即可得到每个节点的最大贡献值。

根据函数 maxGain 得到每个节点的最大贡献值之后,如何得到二叉树的最大路径和?对于二叉树中的一个节点,该节点的最大路径和取决于该节点的值与该节点的左右子节点的最大贡献值,如果子节点的最大贡献值为正,则计入该节点的最大路径和,否则不计入该节点的最大路径和。维护一个全局变量 maxSum 存储最大路径和,在递归过程中更新 maxSum 的值,最后得到的 maxSum 的值即为二叉树中的最大路径和。

图解

代码展示:

JAVA版本
import java.util.*;

/*
 * public class TreeNode {
 *   int val = 0;
 *   TreeNode left = null;
 *   TreeNode right = null;
 * }
 */

public class Solution {
    /**
     * 
     * @param root TreeNode类 
     * @return int整型
     */
    int maxSum = Integer.MIN_VALUE;
    public int maxPathSum (TreeNode root) {
        // write code here
        maxGain(root);
        return maxSum;
    }
    public int maxGain(TreeNode node) {
        if (node == null) {
            return 0;
        }
        
        // 递归计算左右子节点的最大贡献值
        // 只有在最大贡献值大于 0 时,才会选取对应子节点
        int leftGain = Math.max(maxGain(node.left), 0);
        int rightGain = Math.max(maxGain(node.right), 0);

        // 节点的最大路径和取决于该节点的值与该节点的左右子节点的最大贡献值
        int priceNewpath = node.val + leftGain + rightGain;

        // 更新答案
        maxSum = Math.max(maxSum, priceNewpath);

        // 返回节点的最大贡献值
        return node.val + Math.max(leftGain, rightGain);
    }
}
Python版本
class Solution:
    def __init__(self):
        self.maxSum = float("-inf")
        
    def maxPathSum(self , root ):
        # write code here
        def maxGain(node):
            if not node:
                return 0
            # 递归计算左右子节点的最大贡献值
            # 只有在最大贡献值大于 0 时,才会选取对应子节点
            leftGain = max(maxGain(node.left), 0)
            rightGain = max(maxGain(node.right), 0)
            
            # 节点的最大路径和取决于该节点的值与该节点的左右子节点的最大贡献值
            priceNewpath = node.val + leftGain + rightGain
            
            # 更新答案
            self.maxSum = max(self.maxSum, priceNewpath)
        
            # 返回节点的最大贡献值
            return node.val + max(leftGain, rightGain)
        maxGain(root)
        return self.maxSum

复杂度分析

时间复杂度:O(N),其中 N 是二叉树中的节点个数。对每个节点访问不超过 2 次。
空间复杂度:O(N),其中 N 是二叉树中的节点个数。空间复杂度主要取决于递归调用层数,最大层数等于二叉树的高度,最坏情况下,二叉树的高度等于二叉树中的节点个数。

算法思想二:深度优先遍历

解题思路:

二叉树:【A,B,C,D,E,null,F】
问题:以A为根节点的这棵树的最大路径和:
单纯的存在于以B为根节点的子树中,单纯的存在于以C为根节点的子树中A节点到达左子树B中某节点的路径,A节点到达右子树C中某节点的路径,左子树B中某节点到达右子树C中某节点的路径,必然经过A节点A节点(假如左、右子树的最大路径均为负数)
因为需要获得从A节点到达其子树中某节点的路径和,所以我们想到深度优先遍历。又因为计算以A为根节点的树的最大路径和计算其子树B、C中的最大路径和函数功能相同,所以同样采用递归算法。

代码展示:

Python版本
class Solution:
    def maxPathSum(self , root ):
        # write code here
        # 初始化 最小值
        res = float('-inf')
        
        def dfs(root):
            nonlocal res
            if not root:
                return float('-inf')
            # 递归左右子树
            left_max = dfs(root.left)
            right_max = dfs(root.right)
            # 分四种情况
            res = max(res, left_max+root.val, right_max+root.val, right_max+left_max+root.val, root.val)
            return max(root.val, root.val+left_max, root.val+right_max)
        # 深度搜索遍历
        dfs(root)
        return res

复杂度分析

时间复杂度:O(N),其中 N 是二叉树中的节点个数。对每个节点访问不超过 2 次。
空间复杂度:O(N),其中 N 是二叉树中的节点个数。空间复杂度主要取决于递归调用层数,最大层数等于二叉树的高度,最坏情况下,二叉树的高度等于二叉树中的节点个数。


全部评论
深度优先遍历,python代码,解释器会有递归深度限制,默认限制值比较小,需要加大一点 sys.setrecursionlimit()
1 回复 分享
发布于 2023-05-24 12:13 上海
第一个解是不是存在问题呀?如果用例是root = [-10,12,20,null,null,15,7],最大值应该是12+(-10)+20+15 = 37,但是题解给出的答案是20+15 = 35,依旧是35
1 回复 分享
发布于 2023-08-25 16:22 湖北
请问这两种思想有本质区别吗?小白没太看出来差别
点赞 回复 分享
发布于 2021-11-21 11:02
太厉害了
点赞 回复 分享
发布于 2022-06-13 17:21

相关推荐

11-29 11:21
门头沟学院 Java
总包48.5w,意想不到的价格
想开了的垂耳兔很喜欢拱白菜:转人工
点赞 评论 收藏
分享
10-17 10:05
已编辑
北华大学 全栈开发
牛客872465272号:掉头发了哥
点赞 评论 收藏
分享
评论
12
4
分享
牛客网
牛客企业服务