有关于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;
}
};