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

题目来源

题目描述

在这里插入图片描述
在这里插入图片描述

题目解析

思路

对于树的题目,有统一的思考方法,那就是站在树(子树)的顶端根节点root思考

那么对于此题,我们思考如下问题:如果当前处在root节点,左右节点应该告诉我们什么信息才能得到答案?

根据题中对路径的定义,对于此题我们来回答以上问题。当我们遍历到树中某个节点时,我希望左子节点告诉我,在左子树中,以左子节点为开始(端点)的路径和最大为多少,同理我也希望右子节点告诉我类似的信息。

如果有了以上信息,再来思考最后一个问题:有了这个信息如何得到答案?

显然,对于当前节点有四个选择:

  • 我自己就是一条路径
  • 只跟左子节点合并成一条路径
  • 只跟右子节点合并成一条路径
  • 以自己为桥梁,跟左、右子节点合并成一条路径

需要注意的是,我们在递归求解的时候,第四种情况是不能作为递归的返回值的,因为它不符合我们对递归所期望返回值的定义(因为此时该子节点并不是拥有最大路径和路径的起点(端点)),但它也是一个可能的解,所以我们用一个全局变量记录上面四种值的最大值,递归结束后,该变量就是答案。

再次强调一下,当节点以自己为桥梁连接两边形成一条路径时,根据路径定义,其祖先节点不再可能加入到这条路径中。也就是说,这种情况下,它是解的一种可能,但不符合我们递归返回值的定义。

代码如下:

class Solution {
    int maxSum  = INT_MIN;
    /**
       * 返回以该节点为端点的最大路径和
       * @param root
       * @return
       */
    int helper(TreeNode *root){
        if(root == NULL){
            return 0;
        }

        int left = helper(root->left);
        int right = helper(root->right);

        // 当前节点有四个选择:
        // 1)独立成线,直接返回自己的值 
        // 2)跟左子节点合成一条路径 
        // 3)跟右子节点合成一条路径
        int ret =  std::max(root->val, root->val + std::max(left, right));
        // 4)以自己为桥梁,跟左、右子节点合并成一条路径
        maxSum  = std::max(maxSum ,  std::max(ret, root->val + left + right));

        return ret;
    }
public:
    int maxPathSum(TreeNode* root) {
        helper(root);
        return maxSum;
    }
};

思路

看个例子:

    4
   / \
  11 13
 / \
7   2

可以很容易就能找到最长路径为 7->11->4->13,那么怎么用递归来找出正确的路径和呢?

  • 根据以往经验,树的递归解法一般都是递归到叶节点,然后开始边处理边回溯到根节点。
  • 假设此时已经递归到了结点7了,其没有左右子节点。因此以7为根节点的子树的最大路径和就是7
  • 然后回溯到节点11,以节点11为根节点的子树,最大路径和为7+11+2=20
  • 但是当回溯到节点4的时候,对于节点11来说,就不能同时取两条路径了,只能取左路径,或者又路径。所以当根节点是4的时候,那么节点11只能取左路径7,因为7大于0
  • 所以,对于每个节点来说,要知道经过其左子节点的path之和大还是经过右子节点的path之和大。
  • 递归函数返回值就可以定义为以当前节点为根节点,到叶节点的最大路径之和,然后全局路径最大值放在参数中,用结果res表示

在递归函数中

  • 如果当前节点不存在,直接返回0。
  • 否则就分别对左右子节点调用递归函数,由于路径和有可能为负数,这里当然不希望假设负的路径和,所以和0相比,取较大的那个,就是要么不加,加就要加正数
  • 然后来更新全局最大值结果res,就是以左子节点为终点的最大path之和加上以右子节点为终点的最大path之和,还要加上当前节点值,这样就组成了一条完整的路径。
  • 而返回值是取left和right中的较大值+当前节点值,因为返回值的定义是以当前结点为终点的 path 之和,所以只能取 left 和 right 中较大的那个值,而不是两个都要
class Solution {
    int maxSum  = INT_MIN;
    /**
       * 返回经过root的单边分支最大和, 即std::max(root, root+left, root+right)
       * @param root
       * @return
       */
    int helper(TreeNode *root){
        if(root == NULL){
            return 0;
        }

        //计算左边分支最大值,左边分支如果为负数还不如不选择
        int left = std::max(0, helper(root->left));
        //计算右边分支最大值,右边分支如果为负数还不如不选择
        int right = std::max(0, helper(root->right));

        // 经过当前节点的最大路径和是不是最大的
        maxSum  = std::max(maxSum ,  root->val + left + right );
        // 返回经过root的单边最大分支给当前root的父节点计算使用
        return root->val + std::max(left, right);
    }
public:
    int maxPathSum(TreeNode* root) {
        helper(root);
        return maxSum;
    }
};

思路

 class Solution {
     // 任何一棵树,必须汇报上来的信息
     struct Info{
         int maxPathSum;
         int maxPathSumFromHead;
         Info(int path, int head) {
             maxPathSum = path;
             maxPathSumFromHead = head;
         }
     };

     Info* process(TreeNode * x){
         if(x == nullptr){
             return nullptr;
         }

         auto left = process(x->left);
         auto right = process(x->right);

         // x 1)只有x 2)x往左扎 3)x往右扎
         int maxPathSumFromHead = x->val;
         if(left != nullptr){
             maxPathSumFromHead = std::max(maxPathSumFromHead, x->val + left->maxPathSumFromHead);
         }
         if(right != nullptr){
             maxPathSumFromHead = std::max( maxPathSumFromHead,x->val + right->maxPathSumFromHead);
         }

         // x整棵树最大路径和 1) 只有x 2)左树整体的最大路径和 3) 右树整体的最大路径和
         int maxPathSum = x->val;
         if(left != nullptr){
             maxPathSum = std::max(maxPathSum, left->maxPathSum);
         }
         if(right != nullptr){
             maxPathSum = std::max(maxPathSum, right->maxPathSum);
         }
        // 4) x只往左扎 5)x只往右扎
         maxPathSum = std::max(maxPathSum, maxPathSumFromHead);
         // 6)一起扎
         if (left != nullptr && right != nullptr && left->maxPathSumFromHead > 0
             && right->maxPathSumFromHead > 0) {
             maxPathSum = std::max(maxPathSum, left->maxPathSumFromHead + right->maxPathSumFromHead + x->val);
         }
         return new Info(maxPathSum, maxPathSumFromHead);
     }
 public:
     int maxPathSum(TreeNode* root) {
         if(root == nullptr){
             return 0;
         }
         return process(root)->maxPathSum;
     }
 };

类似题目

题目
leetcode:124. 二叉树中的最大路径和 (从父到子,不能子到父) Binary Tree Maximum Path Sum
leetcode:687. 最长同值路径 (从父到子,不能子到父) Longest Univalue Path
leetcode:250. 统计同值子树
  1. 从父到子,不能子到父

(1)带上x的最大路径和:

  • 只有x
  • x + 左子树
  • x + 右子树

(2)整颗树的最大路径和

  • 只有x
  • 左树整体最大和
  • 右树整体最大和
  • x + 左子树 + 右子树(当节点以自己为桥梁连接两边形成一条路径时,根据路径定义,其祖先节点不再可能加入到这条路径中,再次提醒: 一条从父节点延伸下来的路径,不能走入左子树又掉头走右子树,不能两头收益
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值