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.
For example:
[1,2,3]
. The optimal path is 2 -> 1 -> 3 with a path sum of 2 + 1 + 3 = 6. Return 6.[-10,9,20,null,null,15,7]
. The optimal path is 15 -> 20 -> 7 with a path sum of 15 + 20 + 7 = 42. Return 42.How would you implement an algorithm to find the maximum path sum in a given binary tree? What is the time and space complexity of your solution? Consider edge cases such as an empty tree or negative node values.
When you get asked this question in a real-life environment, it will often be ambiguous (especially at FAANG). Make sure to ask these questions in that case:
The brute force strategy for finding the maximum path sum in a binary tree is to explore every possible path. We calculate the sum of each path and then compare these sums to find the largest one. This involves generating all potential paths and checking each one.
Here's how the algorithm would work step-by-step:
def max_path_sum_brute_force(root):
maximum_sum = float('-inf')
def all_paths_from_node(node):
if not node:
return []
# Base case: leaf node
if not node.left and not node.right:
return [[node.val]]
left_paths = all_paths_from_node(node.left)
right_paths = all_paths_from_node(node.right)
all_paths = [[node.val] + path for path in left_paths] + \
[[node.val] + path for path in right_paths]
# Add the node itself as a path
all_paths.append([node.val])
return all_paths
def calculate_path_sum(path):
return sum(path)
all_possible_paths = []
def collect_paths(node):
nonlocal all_possible_paths
if node:
all_possible_paths.extend(all_paths_from_node(node))
collect_paths(node.left)
collect_paths(node.right)
collect_paths(root)
#Iterate through each path to calculate it's total sum
for path in all_possible_paths:
current_path_sum = calculate_path_sum(path)
#Store the maximum path sum found so far.
maximum_sum = max(maximum_sum, current_path_sum)
return maximum_sum
The core idea is to traverse the tree and calculate the maximum path sum in a bottom-up manner. Each node computes the best path sum that includes itself and possibly one of its children. The global maximum path sum is updated during this process.
Here's how the algorithm would work step-by-step:
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def max_path_sum(root):
maximum_path_sum = float('-inf')
def max_path_sum_recursive(node):
nonlocal maximum_path_sum
if not node:
return 0
# Recursively calculate the max path sum for the left and right subtrees
left_max_sum = max(0, max_path_sum_recursive(node.left))
right_max_sum = max(0, max_path_sum_recursive(node.right))
# Update the global maximum path sum
# considering the current node as the root of the path.
price_newpath = node.val + left_max_sum + right_max_sum
maximum_path_sum = max(maximum_path_sum, price_newpath)
# Return the maximum path sum that includes the current node
# and one of its children. Crucial for connecting to the parent.
return node.val + max(left_max_sum, right_max_sum)
max_path_sum_recursive(root)
# The maximum_path_sum has been updated during recursive calls.
return maximum_path_sum
Case | How to Handle |
---|---|
Null root node | Return negative infinity to not contribute to max sum. |
Single node tree | Return the node's value directly. |
Tree with all negative values | Handle correctly by selecting the largest (least negative) single node value as the maximum path sum. |
Tree with all zero values | Handle correctly; the maximum path sum will be zero. |
Tree with extremely large positive values potentially causing integer overflow | Use long data type to prevent potential integer overflow issues when calculating path sums. |
Skewed tree (all nodes on one side) | Recursively traverse the skewed tree correctly and update the maximum path sum. |
Tree with a long path of small positive values and a single large negative value | The algorithm should correctly identify if including the negative value reduces the overall maximum sum or not. |
Deeply nested tree potentially leading to stack overflow due to excessive recursion | Consider alternative iterative approach to manage call stack if necessary. |