Problem
A path in a binary tree is a sequence of nodes where each pair of adjacent nodes in the sequence has an edge connecting them. A node can only appear in the sequence at most once. Note that the path does not need to pass through the root.
The path sum of a path is the sum of the node’s values in the path.
Given the root
of a binary tree, return the maximum path sum of any non-empty path.
Examples
Example 1:
1
/ \
2 3
Input: root = [1,2,3]
Output: 6
Explanation: The optimal path is 2 -> 1 -> 3 with a path sum of 2 + 1 + 3 = 6.
Example 2:
-10
/ \
9 20
/ \
15 7
Input: root = [-10,9,20,null,null,15,7]
Output: 42
Explanation: The optimal path is 15 -> 20 -> 7 with a path sum of 15 + 20 + 7 = 42.
Solution
Method 1 - DFS
For each node, there are four possible paths for the maximum path sum:
- Only the node itself
- The maximum path through the left child plus the node
- The maximum path through the right child plus the node
- The maximum path through the left child, the node, and the right child
The key is to track these four paths and select the highest value at the end.
Note
It is important to understand that each subtree’s root must return the maximum path sum that includes at most one child. This is necessary for the call to the parent function. In the code below, this sum is called maxOne
and it is what the recursive function returns.
Why don’t we return the sum of node.val + node.left + node.right
(named currTwo
) in the last line? Including currTwo
in the maxPathSum
would violate the principle that each node can be traversed only once. Since it is impossible to traverse from node->left->right->node’s parent without revisiting the node itself, we cannot include currTwo
in the return value.
Code
Java
Using Array as object
We can also use an array to store value for recursive methods. As, java is not pass by value, we have to use array
or AtomicInteger
.
public int maxPathSum(TreeNode root) {
int max[] = new int[1];
max[0] = Integer.MIN_VALUE;
calculateSum(root, max);
return max[0];
}
public int calculateSum(TreeNode root, int[] max) {
if (root == null) return 0;
int leftMax = calculateSum(root.left, max);
int rightMax = calculateSum(root.right, max);
int maxOne = Math.max(root.val, root.val + Math.max(leftMax, rightMax));
int currTwo = leftMax + root.val + rightMax;
max[0] = Math.max(max[0], Math.max(maxOne, currTwo));
return maxOne;
}
We can also use stream api to compare max of 2+ values:
public int calculateSum(TreeNode root, int[] max) {
if (root == null) return 0;
int left = calculateSum(root.left, max);
int right = calculateSum(root.right, max);
int maxOne = Stream.of(root.val, root.val + left, root.val + right).max().getInteger();
int currTwo = leftMax + root.val + rightMax;
max[0] = Stream.of(max[0], maxOne, currTwo).max().getInteger();
return maxOne;
}
Using Global Variable
public class BinaryTreeMaximumPathSum {
private int maxSum;
public int maxPathSum(TreeNode root) {
maxSum = Integer.MIN_VALUE;
maxSumHelper(root);
return maxSum; // as maxSum will always store the result
}
public int maxSumHelper(TreeNode root) {
// base case
if (root == null) return 0;
// recursing through left and right subtree
int leftMax = maxSumHelper(root.left);
int rightMax = maxSumHelper(root.right);
// finding all the four paths and the maximum between all of them
int maxRightLeft = Math.max(leftMax, rightMax);
int maxOne = Math.max(root.val, (root.val + maxRightLeft));
int maxAll = Math.max(maxOne, leftMax + rightMax + root.val);
// Storing the result in the maxSum holder
maxSum = Math.max(maxSum, maxAll);
// returning the value if root was part of the answer
return maxOne;
}
}
Examples
Example 1
Our goal is to find the maximum path sum. Now consider the following example.
10
/ \
null null
In this simple case we know that the max sum would be just the root node itself and the answer would be 10. So fo all leaf
nodes the max sum path is the value of the node itself.
Example 2
Now let’s consider the following example.
20
/ \
10 30
Here there are multiple possibilities and we need to take care of the following FOUR PATHS that could be our max.
- The root iself :
20
- The root with the maximum from it’s left subTree : = 10 + 20
20
/
10
- The root with the maximum from it’s right subTree : = 30 + 20
20
\
30
- The root with it’s left, right and itself = 60
20
/ \
10 30
In case you are wondering why we did not choose the root.left (10) or root.right(30) alone in the calculation ( like I wondered ), that’s because we would have already computed the result of it as a node in our recursion separately.