LeetCode二叉树中的最大路径和

124. 二叉树中的最大路径和

路径 被定义为一条从树中任意节点出发,沿父节点-子节点连接,达到任意节点的序列。同一个节点在一条路径序列中 至多出现一次 。该路径 至少包含一个 节点,且不一定经过根节点。

路径和 是路径中各节点值的总和。

给你一个二叉树的根节点 root ,返回其 最大路径和 

示例 1

输入:root = [1,2,3]

输出:6

解释:最优路径是 2 -> 1 -> 3 ,路径和为 2 + 1 + 3 = 6

示例 2

输入:root = [-10,9,20,null,null,15,7]

输出:42

解释:最优路径是 15 -> 20 -> 7 ,路径和为 15 + 20 + 7 = 42

思路:

  这道题是一个困难题。因为它确实不像传统二叉树的方法,简单写一下递归就可以得到结果。无法单单用递归解决的原因在于在一棵二叉树里画的路径可以是各种各样的——可以直接从根节点沿着一条路线画到子节点;也可以从某个子节点起,往上行走至某中间节点,再下落到另一个子节点上……可见,我们的路径可以是各种各样,尤其是题目中的节点还有负数的情况,这也让整个问题看起来十分复杂。

  首先说明,下面的题解会写的很“勉强”(这也是我自己写的比较别扭的一篇思路了),所以千万不要钻牛角尖。我还是建议一半理解+一半记忆。

  虽然我们可能的“最大路径”多种多样,但我们总归可以将他们分成两类:可以用递归寻找(“竖着”的路径) 以及 不可以用递归寻找(“横着”的路径)

  对于前者,我们就照常使用我们平时解决二叉树的方法去写递归;对于后者,我们开设一个全局变量来实时更新存储。最终我们将两类的总结果进行比较,得到最大值。

  具体来看,我们把问题缩小,一个大二叉树,我们想象成一棵棵只有根、左节点、右节点的小树。这一棵棵的小树构成大树,我们只需要考虑小树即可。按照题意:一颗三个节点的小树的结果只可能有如下6种情况:

根 + 左 + 右

根 + 左

根 + 右

要看能否进行递归寻找,就要看能否从根节点向下延伸

  其中可以递归查找的情况有: +  + 这三种,可以想象成拿着笔从根出发,最起码可以向左向右走或者不动,起码可以保持路径是“竖着”的。可以发现一定要包含根节点,这样我们定义递归函数的时候,就是传入一个节点,传出以传入节点为根节点的最大路径

  而不可以递归查找的情况有:++这三种,对于只有左或者右的情况,我们都无法碰到根节点,自然无法递归延伸;对于根,左,右都存在的情况,明显是一个“横着”的路径,所以也是无法通过延伸递归来获得的。

  好了,点到为止!就算没有完全看懂思路也没关系,我们直接看代码!很好理解和记忆:

代码:

#先把情况列到最上面作为参考

# 根 + 左 + 右

# 根 + 左

# 根 + 右

# 根

# 左

# 右

class Solution(object):

    def maxPathSum(self, root):

        no_di=[float('-inf')]#no_di存放非递归最大

        def di(root):#di返回以root为根节点的最大路径

            if not root:#老递归了,定义base case

                return float('-inf'#因为节点值有负的 所以用负无穷 不能用0

            right = di(root.right)#获得以它右节点为根节点的最大路径

            left = di(root.left)#获得以它左节点为根节点的最大路径
            #左,右,根+左+右

            no_di[0] = max(no_di[0],root.val+right+left,right,left)#非递归
            #根,根+左,根+右

            di_=max(root.val,root.val+left,root.val+right) # di_存放递归最大

            return di_

        new_max=di(root)#不能把这个di(root)直接放到下面的return

        #因为di函数里要修改no_di[0],所以他要先执行

        return max(no_di[0],new_max)

小结:

  我们单看递归的部分(不去管no_di即非递归相关),可以发现函数di(root)的功能就是传入节点root,计算其左节点的最大路径和右节点的最大路径,然后计算max(自身值,自身值+左,自身值+右)作为di(root)的返回值。这样子就可以完美的计算出“竖直”方向上的最大路径了。

  而“横着”的路径,则在每次递归函数中尝试与全局变量进行更新,比较巧妙地放在了递归函数里。

  代码中要注意因为我们的节点有负数的情况,因此初始化最大值要用负无穷float(‘-inf’)而不是0。此外,存放非递归最大的变量no_di我用的一个列表来存放,这样可以充当全局变量的作用,否则在leetcode里面可能会找不到该变量,算是一个小技巧,之前也提到过。

  ……

  2021年的最后一个小时,留个记录,祝岁月静好,国泰民安~

posted @   JunanP  阅读(18)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
点击右上角即可分享
微信分享提示