Skip to content

Commit 30726b9

Browse files
Add 'Binary Tree Maximum Path Sum'
1 parent 0ccde12 commit 30726b9

File tree

3 files changed

+131
-0
lines changed

3 files changed

+131
-0
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ A collection of LeetCode solutions
1212

1313
[Binary Tree Right Side View](./src/binary_tree_right_side_view.py)
1414

15+
[Binary Tree Maximum Path Sum](./src/binary_tree_maximum_path_sum.py)
16+
1517
[Climbing Stairs](./src/climbing_stairs.py)
1618

1719
[Coin Change](./src/coin_change.py)

src/binary_tree_maximum_path_sum.py

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
"""
2+
124. Binary Tree Maximum Path Sum
3+
4+
https://leetcode.com/problems/binary-tree-maximum-path-sum
5+
6+
NOTES
7+
* This one is hard. I had to read through the entire solution, however, once
8+
you understand the core algorithm, it is relatively straightforward.
9+
10+
First, let's define a "path" in a tree:
11+
12+
>A path in a binary tree is a sequence of nodes where each pair of adjacent
13+
nodes in the sequence has an edge connecting them.
14+
15+
Each node in a path can be connected to 0, 1, or 2 nodes. A path does not need
16+
to pass through the root of the tree. A path can also be a single node.
17+
18+
The following are all valid paths: The following is not a valid path:
19+
20+
21+
● ● - ● ● \
22+
/ \ ●
23+
● ● / \
24+
● ●
25+
26+
From this we observe that the maximum path sum of a tree is calculated using
27+
one of the following cases:
28+
29+
1. A single node (root).
30+
2. The maximum path of the left subtree plus the root.
31+
3. The maximum path of the right subtree plus the root.
32+
4. The maximum path of both subtrees plus the root.
33+
34+
The minimal subproblem (or base case) is a null node, which has a maximum path
35+
sum of 0. Next, we consider the case of a single node, which itself may
36+
constitute the maximum path sum. From here, cases 2, 3, and 4 can be easily
37+
calculated, and a maximum sum path of the subtree can be determined. This
38+
process represents a bottom-up approach, which can be solved nicely using
39+
recursion and, in particular, using a depth-search traversal of the tree.
40+
41+
NOTE: If we take a top-down approach, that is, enumerating all paths in the
42+
tree starting from root, the time complexity is exponential (O(n^2)), since a
43+
complete binary tree with n nodes has (n^2 - 1)/2 unique paths.
44+
45+
Another insight is that in order to compare the maximum path sum for cases 2,
46+
3, and 4, we need to determine the maximum path of the left and right subtrees
47+
first. This requires a post-order traversal:
48+
49+
1. Recursively traverse the current node's left subtree.
50+
2. Recursively traverse the current node's right subtree.
51+
3. Visit the current node.
52+
53+
Combining this information, the recursive logic is relatively straightforward:
54+
55+
1. Calculate the maximum sum path of the left and right subtrees.
56+
2. Check if the current maximum path sum is the overall maximum path sum.
57+
3. Return the current maximum path sum for the subtree.
58+
"""
59+
60+
import sys
61+
62+
from src.classes import TreeNode
63+
64+
65+
class Solution:
66+
def maxPathSum(self, root: TreeNode | None) -> int:
67+
max_path_sum = -sys.maxsize
68+
69+
def _maxPathSum(root: TreeNode | None) -> int:
70+
"""
71+
Performs a post-order traversal of the tree rooted at `root` and
72+
recursively calculates the maximum sum path for the tree.
73+
"""
74+
nonlocal max_path_sum
75+
76+
if not root:
77+
return 0
78+
79+
# Calculate the maximum path sum of the left subtree.
80+
max_path_sum_left = _maxPathSum(root.left)
81+
82+
# Calculate the maximum path sum of the right subtree.
83+
max_path_sum_right = _maxPathSum(root.right)
84+
85+
# Calculate the maximum path sum of the current subtree.
86+
# Checks the four possible cases.
87+
max_path_sum_curr = max(
88+
root.val,
89+
max_path_sum_left + root.val,
90+
max_path_sum_right + root.val,
91+
max_path_sum_left + max_path_sum_right + root.val,
92+
)
93+
94+
# Update the maximum path sum of all paths.
95+
max_path_sum = max(max_path_sum, max_path_sum_curr)
96+
97+
# NOTE: A node cannot have more than 2 connections, so the returned
98+
# value is the maximum path of the root or the root plus the left
99+
# or right subtree.
100+
return max(
101+
root.val,
102+
max_path_sum_left + root.val,
103+
max_path_sum_right + root.val,
104+
)
105+
106+
_maxPathSum(root)
107+
return max_path_sum
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
"""
2+
124. Binary Tree Maximum Path Sum
3+
4+
https://leetcode.com/problems/binary-tree-maximum-path-sum
5+
"""
6+
7+
from unittest import TestCase
8+
9+
from src.binary_tree_maximum_path_sum import Solution
10+
from tests.utils import create_binary_tree_from_list
11+
12+
13+
class TestSolution(TestCase):
14+
def test_1(self):
15+
exp = 6
16+
root = create_binary_tree_from_list([1, 2, 3])
17+
assert Solution().maxPathSum(root) == exp
18+
19+
def test_2(self):
20+
exp = 42
21+
root = create_binary_tree_from_list([-10, 9, 20, None, None, 15, 7])
22+
assert Solution().maxPathSum(root) == exp

0 commit comments

Comments
 (0)