LeetCode124. Binary Tree Maximum Path Sum

有关于leetcode的刷题记录。

这次是124题。首先看题干:
···

Given a non-empty binary tree, find the maximum path sum.

For this problem, a path is defined as any sequence of nodes from some starting node to any node in the tree along the parent-child connections. The path must contain at least one node and does not need to go through the root.

···   

首先很明确,这个问题递归是天然的(树为递归结构)。理论上dynamic programming可行,但是找不到合适的存储结构(新造一棵树也可以但是其实很麻烦)。

然后思考算法细节。任意一个节点上的路径最大和 = max(左子树路径最大和,右子树路径最大和,包括本节点的路径最大和,在之前已经产生的路径最大和)。左子树和右子树的和交给递归,已经产生的路径最大和可以用一个引用参数来搞定,剩下的就是包括本节点的路径最大和。请注意:递归函数的返回值不能是本节点上的路径最大和,而应该是包括本节点的路径最大和的剪枝后的结果。解释一下这句话:首先,当你想要获取左子树路径最大和的时候,你必然是想要包括左子树节点的路径最大和,而不是左子树下所有路径里某一路径的最大和。因为如果不包括左子节点,那么就不符合题目要求:你的路径不是连续的。右子树同理。所以你返回的应该是包括了本节点的路径最大和。但是题目又要求我们只能选一条路径,所以如果左右子树都被你添加到了最大和中,你就必须进行剪枝:也就是删掉其中一个子树的最大和。请注意:我们返回的值本质是:一条唯一的不带分叉的,包括了本节点的路径上的所有节点的和。所以你不能既选左子树又选右子树。

我们的结果并不是函数的返回值,而是函数的引用参数,也就是之前已经产生的路径最大和那个参数,设为res。剩下的请看代码:

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    int maxPathSum(TreeNode* root) {
        int res = INT_MIN;
        getMaxSum(root, res);
        return res;
    }
    
    int getMaxSum(TreeNode* node, int & max) {
        if(!node) {
            return INT_MIN; // 空节点为递归基
        }
        int left_sum_raw = getMaxSum(node->left, max);
        int right_sum_raw = getMaxSum(node->right, max);

        /** 左子节点或右子节点为空时,我们实际上希望的是返回值为0.
          * 但是有可能整棵树的节点值都是负数,0反而是最大值,这不科学。
          * 所以只能返回INT_MIN并进行处理。
        */
        int left_sum = (left_sum_raw == INT_MIN)?0:left_sum_raw;
        int right_sum = (right_sum_raw == INT_MIN)?0:right_sum_raw;
        int mid_sum = node->val;
        bool left_add = false, right_add = false;

        // 剪枝判定标志
        if(left_sum > 0) {
            mid_sum += left_sum;
            left_add = true;
        }
        if(right_sum > 0) {
            mid_sum += right_sum;
            right_add = true;
        }
        max = std::max(max, std::max(mid_sum, std::max(left_sum_raw, right_sum_raw)));

        // 如果左子节点和右子节点都加过,选择小的那个cut掉
        if(left_add && right_add)
            mid_sum -= (left_sum > right_sum)?right_sum:left_sum;
        return mid_sum;
    }
};

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值