目录
题目
路径 被定义为一条从树中任意节点出发,沿父节点-子节点连接,达到任意节点的序列。同一个节点在一条路径序列中 至多出现一次 。该路径 至少包含一个 节点,且不一定经过根节点。
路径和 是路径中各节点值的总和。
给你一个二叉树的根节点 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
提示:
- 树中节点数目范围是
[1, 3 * 104]
-1000 <= Node.val <= 1000
分析
本题考察的内容为树的递归遍历,题目中明确了树不为空,但是节点可能为负的情况,即左左右孩子不一定对父节点都是增益效果,所以我们需要对其进行判断。
假设现在有一个子树,节点分别为a、b、c,借助DP的思路,我们将问题拆分成2种大的情况。
a
/ \
b c
(1)a为最终结果所确定的子树的一部分
(1.1) a选取左右子树的最大路径和较大的那个组成一个路径。
(1.2) 左右子树均的最大路径和均为负数,均不选取,a为最终路径的叶子节点。
(2)a为最终结果所确定的子树的根节点
(2.1) 左右子树的最大路径和均为正数,a的左右子树都在最终结果中。
(2.2)左右子树的最大路径和存在负数(可能一个为负,可能都为负),a只有左/右子树,或只有根节点。
解答
对于上文中分析后得出的第一种情况,我们用max[a]代表a节点能贡献给父节点的最大路径,第二种情况将MAX[a]代表到a节点为止的最大路径和,于是有:
max[a] = Math.max(max[a.left]+a,max[a.right]+a,a);
MAX[a] = Math.max(max[a],a+max[b]+max[c]);
MAX(最终结果) = Math.max(MAX(当前最大值),MAX[a]);
我们通过深度优先遍历DFS,遍历每一个节点,每次递归结束后将max(a)返回给上一层,
用于作为子树的最大路径和参与a的父节点的相关计算。
class Solution {
// 题目允许节点值为负数,这里进行特殊处理
int MAX = Integer.MIN_VALUE;
public int maxPathSum(TreeNode root) {
dfs(root);
return MAX;
}
public int dfs(TreeNode root){
if(root==null){
return Integer.MIN_VALUE;
}
// 计算左右子树能提供的最大路径
int left = dfs(root.left);
int right = dfs(root.right);
// 计算第一种情况,即当前节点作为最终结果的子路径,此时只能在左右子树中选较大的那个,或者都不选取
int max = Math.max(Math.max(left,right)+root.val,root.val);
// 计算第二种情况,即当前节点作为最终结果的根节点,那么还需要在上一步的基础上再将其与
// 根节点+左子树+右子树进行比较,并将结果再与当前的最大路径和进行比较即可。
MAX = Math.max(MAX,Math.max(max,root.val+left+right));
return max;
}
}
时间复杂度:O(n)
空间复杂度:O(n)
自测
题目给出的数据范围表明,树不为空,那么空树的情况我们可以直接跳过。但需要注意这里树的节点是可能为负数的,所以我们需要考虑到树的节点全为负,或部分为负数的情况。另外树的特殊情况,如每个节点都只有左子树这样的情形也需要考虑到。
补充
相关题目:543. 二叉树的直径
这题求的是路径的长度,我们将本题中计算最大和的步骤调整为每次递归返回时+1即可得到结果。
class Solution {
int res=0;
public int diameterOfBinaryTree(TreeNode root) {
def(root);
return res;
}
public int def(TreeNode root){
if(root==null) return 0;
int left=def(root.left);
int right=def(root.right);
res = Math.max(left+right,res);
return Math.max(left,right)+1;
}
}