题目来源
题目描述
题目解析
思路
对于树的题目,有统一的思考方法,那就是站在树(子树)的顶端根节点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)带上x的最大路径和:
- 只有x
- x + 左子树
- x + 右子树
(2)整颗树的最大路径和
- 只有x
- 左树整体最大和
- 右树整体最大和
- x + 左子树 + 右子树(当节点以自己为桥梁连接两边形成一条路径时,根据路径定义,其祖先节点不再可能加入到这条路径中,再次提醒: 一条从父节点延伸下来的路径,不能走入左子树又掉头走右子树,不能两头收益)