Given the root of a non-empty binary tree, return the maximum path sum of any non-empty path.
A path in a binary tree is a sequence of nodes where each pair of adjacent nodes has an edge connecting them. A node can not appear in the sequence more than once. The path does not necessarily need to include the root.
The path sum of a path is the sum of the node's values in the path.
Example 1:
Input: root = [1,2,3]
Output: 6Explanation: The path is 2 -> 1 -> 3 with a sum of 2 + 1 + 3 = 6.
Example 2:
Input: root = [-15,10,20,null,null,15,5,-5]
Output: 40Explanation: The path is 15 -> 20 -> 5 with a sum of 15 + 20 + 5 = 40.
Constraints:
1 <= The number of nodes in the tree <= 1000.-1000 <= Node.val <= 1000
You should aim for a solution with O(n) time and O(n) space, where n is the number of nodes in the given tree.
A brute force solution would involve checking the path sum between every pair of nodes in the tree, leading to an O(n^2) time complexity. Can you think of a more efficient approach? Consider what information you would need at each node to calculate the path sum if it passes through the current node.
At a node, there are three scenarios to compute the maximum path sum that includes the current node. One includes both the left and right subtrees, with the current node as the connecting node. Another path sum includes only one of the subtrees (either left or right), but not both. Another considers the path sum extending from the current node to the parent. However, the parent’s contribution is computed during the traversal at the parent node. Can you implement this?
We can use the Depth First Search (DFS) algorithm to traverse the tree. We maintain a global variable to track the maximum path sum. At each node, we first calculate the maximum path sum from the left and right subtrees by traversing them. After that, we compute the maximum path sum at the current node. This approach follows a post-order traversal, where we visit the subtrees before processing the current node.
We return the maximum path sum from the current node to its parent, considering only one of the subtrees (either left or right) to extend the path. While calculating the left and right subtree path sums, we also ensure that we take the maximum with 0 to avoid negative sums, indicating that we should not include the subtree path in the calculation of the maximum path at the current node.
Before attempting this problem, you should be comfortable with:
For each node, consider it as the highest point of a potential path.
A path can pass through a node as:
left-subtree → node → right-subtree
So for every node we need two things:
A downward path ends at that child and goes only downward (no turning back up).
This is computed using getMax().
Then we compute the best full path through this node:
node.val + leftDown + rightDownWe try this for every node using DFS and update the global answer.
DFS to visit each node.node.val + leftDown + rightDowngetMax(node) returns the best downward path:node.val + max(leftDown, rightDown)0 if it is negative (since negative paths should be ignored).# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def maxPathSum(self, root: Optional[TreeNode]) -> int:
res = -float('inf')
def dfs(root):
nonlocal res
if not root:
return
left = self.getMax(root.left)
right = self.getMax(root.right)
res =max(res, root.val + left + right)
dfs(root.left)
dfs(root.right)
dfs(root)
return res
def getMax(self, root: Optional[TreeNode]) -> int:
if not root:
return 0
left = self.getMax(root.left)
right = self.getMax(root.right)
path = root.val + max(left, right)
return max(0, path)In the maximum path sum problem, a path can start and end anywhere in the tree, but it must go downward at each step (parent → child).
For every node, two values matter:
Max Downward Path starting at this node
node.val + max(leftDown, rightDown)Max Path Through This Node
node.val + leftDown + rightDownWhile computing DFS:
0), because adding negative values only makes the path worse.This ensures each node is visited once — O(n) optimal time.
res, initialized with the root’s value.dfs(node):None, return 0.leftMax = dfs(node.left)
rightMax = dfs(node.right)leftMax = max(leftMax, 0)
rightMax = max(rightMax, 0)res = max(res, node.val + leftMax + rightMax)node.val + max(leftMax, rightMax)dfs(root) and return res.# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def maxPathSum(self, root: Optional[TreeNode]) -> int:
res = [root.val]
def dfs(root):
if not root:
return 0
leftMax = dfs(root.left)
rightMax = dfs(root.right)
leftMax = max(leftMax, 0)
rightMax = max(rightMax, 0)
res[0] = max(res[0], root.val + leftMax + rightMax)
return root.val + max(leftMax, rightMax)
dfs(root)
return res[0]When all node values are negative, the maximum path sum is still negative. Initializing the result to 0 causes incorrect answers for trees with all negative values.
# Wrong: fails for trees like [-3]
res = 0 # should be: res = float('-inf') or root.valA subtree with negative sum should not be included in the path. Failing to take max(0, subtree_sum) adds negative values that reduce the total.
# Wrong: includes negative contributions
leftMax = dfs(root.left) # should be: max(dfs(root.left), 0)The recursive function must return only one branch (left OR right) to the parent, not both. Returning node.val + leftMax + rightMax allows invalid paths that fork at multiple nodes.
# Wrong: returns both branches (creates invalid forking path)
return node.val + leftMax + rightMax
# Correct: return node.val + max(leftMax, rightMax)A valid path can be just one node. The algorithm must consider node.val alone as a potential maximum, especially when both subtrees contribute negative values.
The maximum path can go through any node as the "turning point" (left subtree -> node -> right subtree). This is different from paths that only go downward from root to leaf.