1373. Maximum Sum BST in Binary Tree
Problem Description
You are given the root of a binary tree. Your task is to find the maximum sum of all node values in any subtree that is also a valid Binary Search Tree (BST).
A Binary Search Tree must satisfy these properties:
- Every node in the left subtree has a value less than the current node's value
- Every node in the right subtree has a value greater than the current node's value
- Both left and right subtrees must themselves be valid BSTs
The problem asks you to examine all possible subtrees within the given binary tree, identify which ones are valid BSTs, calculate the sum of node values for each valid BST, and return the maximum sum found.
For example, if you have a tree where only certain subtrees are valid BSTs, you would:
- Check each subtree to determine if it's a valid BST
- For each valid BST found, calculate the sum of all its node values
- Track the maximum sum encountered
- Return this maximum value
Note that:
- A single node by itself is considered a valid BST with sum equal to its value
- An empty subtree is considered a valid BST with sum
0
- The entire tree might not be a BST, but some of its subtrees could be
- You need to find the maximum sum among all valid BST subtrees
The solution uses a depth-first search approach that returns four values for each subtree: whether it's a BST, its minimum value, maximum value, and sum. This information helps determine if parent nodes can form valid BSTs with their children.
Flowchart Walkthrough
First, let's pin down the algorithm using the Flowchart. Here's a step-by-step walkthrough:
Is it a graph?
- Yes: A binary tree is a special type of graph where each node has at most two children and there are no cycles.
Is it a tree?
- Yes: The problem explicitly states we're working with a binary tree structure.
DFS
- Since we answered "yes" to the tree question, the flowchart leads us directly to DFS (Depth-First Search).
Conclusion: The flowchart suggests using a DFS approach for this problem.
This makes perfect sense for our Maximum Sum BST problem because:
- We need to traverse the entire tree to examine all possible subtrees
- We need to process information from children before determining if a parent node forms a valid BST (bottom-up approach)
- DFS allows us to recursively check each subtree, validate BST properties, and calculate sums
- The recursive nature of DFS perfectly matches the recursive structure of determining BST validity (checking left subtree, right subtree, then current node)
The DFS pattern enables us to:
- Visit each node exactly once
- Gather information from child nodes (min value, max value, sum, BST status)
- Use this information to determine if the current subtree is a valid BST
- Track the maximum sum across all valid BST subtrees encountered
Intuition
To find the maximum sum of any BST subtree, we need to check every possible subtree in the given binary tree. The key insight is that we can't just check if a subtree is a BST in isolation - we need information from the children to make this determination.
Think about what makes a tree a BST: the root value must be greater than all values in its left subtree and less than all values in its right subtree. This means we need to know the maximum value from the left subtree and the minimum value from the right subtree to validate the BST property at any node.
This naturally leads to a bottom-up approach using DFS. Starting from the leaves and working our way up allows us to gather the necessary information from children before processing the parent.
For each node, we need to track four pieces of information:
- Is this subtree a valid BST? - We can only form a BST at the current node if both children are BSTs
- What's the minimum value in this subtree? - Parent nodes need this to check BST validity
- What's the maximum value in this subtree? - Parent nodes need this to check BST validity
- What's the sum of all nodes in this subtree? - We need this to track the maximum sum
The beauty of this approach is that when we're at a node and both its subtrees are valid BSTs, we just need to check if left_max < node.val < right_min
. If this condition holds, the current subtree is also a BST, and we can calculate its sum as left_sum + right_sum + node.val
.
For empty nodes (base case), we return values that won't interfere with BST validation: (True, +∞, -∞, 0)
. The infinity values ensure that any comparison with actual node values will satisfy the BST conditions.
As we traverse the tree, we keep updating a global maximum whenever we find a valid BST, ensuring we capture the largest sum among all valid BST subtrees.
Learn more about Tree, Depth-First Search, Binary Search Tree, Dynamic Programming and Binary Tree patterns.
Solution Approach
The solution implements a DFS traversal that returns a quadruple (bst, mi, mx, s)
for each subtree, where:
bst
: Binary flag (1 if the subtree is a BST, 0 otherwise)mi
: Minimum value in the subtreemx
: Maximum value in the subtrees
: Sum of all nodes in the subtree
Base Case:
When we encounter an empty node (root is None
), we return (1, inf, -inf, 0)
. The infinity values are chosen strategically - any real node value will be less than inf
and greater than -inf
, ensuring the BST property checks pass for leaf nodes.
Recursive Case: For each node, we:
- Recursively call
dfs
on the left child, getting(lbst, lmi, lmx, ls)
- Recursively call
dfs
on the right child, getting(rbst, rmi, rmx, rs)
BST Validation: A subtree rooted at the current node is a BST if and only if:
- Both left and right subtrees are BSTs:
lbst == 1
andrbst == 1
- The current node's value is greater than the maximum value in the left subtree:
lmx < root.val
- The current node's value is less than the minimum value in the right subtree:
root.val < rmi
When the subtree is a valid BST:
- Calculate the sum:
s = ls + rs + root.val
- Update the global maximum:
ans = max(ans, s)
- Return the tuple representing this valid BST:
- BST flag:
1
- Minimum value:
min(lmi, root.val)
(could be from left subtree or current node) - Maximum value:
max(rmx, root.val)
(could be from right subtree or current node) - Sum:
s
- BST flag:
When the subtree is NOT a valid BST:
Return (0, 0, 0, 0)
. The actual values for min, max, and sum don't matter since the BST flag is 0, indicating this subtree won't be considered by parent nodes.
Global Answer Tracking:
The variable ans
is maintained globally (using nonlocal
in Python) to track the maximum sum encountered across all valid BST subtrees. It's initialized to 0 and updated whenever we find a valid BST.
The algorithm visits each node exactly once, making it O(n) in time complexity, where n is the number of nodes in the tree.
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through a small example to illustrate the solution approach.
Consider this binary tree:
4 / \ 2 5 / \ 1 3
We'll trace through the DFS traversal to see how it identifies valid BSTs and finds the maximum sum.
Step 1: DFS reaches leaf node 1
- Since node 1 has no children, both left and right return
(1, inf, -inf, 0)
- Check BST validity:
-inf < 1 < inf
✓ (both children are BSTs) - This subtree is a BST with sum = 0 + 0 + 1 = 1
- Update global max: ans = max(0, 1) = 1
- Return:
(1, 1, 1, 1)
Step 2: DFS reaches leaf node 3
- Similar to node 1, returns
(1, 3, 3, 3)
- Update global max: ans = max(1, 3) = 3
Step 3: DFS processes node 2
- Left child (node 1) returned:
(1, 1, 1, 1)
- Right child (node 3) returned:
(1, 3, 3, 3)
- Check BST validity:
- Both children are BSTs ✓
- Left max (1) < current (2) ✓
- Current (2) < right min (3) ✓
- This subtree is a BST with sum = 1 + 3 + 2 = 6
- Update global max: ans = max(3, 6) = 6
- Return:
(1, min(1,2), max(3,2), 6)
=(1, 1, 3, 6)
Step 4: DFS reaches leaf node 5
- Returns
(1, 5, 5, 5)
- Update global max: ans = max(6, 5) = 6
Step 5: DFS processes root node 4
- Left child (node 2) returned:
(1, 1, 3, 6)
- Right child (node 5) returned:
(1, 5, 5, 5)
- Check BST validity:
- Both children are BSTs ✓
- Left max (3) < current (4) ✓
- Current (4) < right min (5) ✓
- This entire tree is a BST with sum = 6 + 5 + 4 = 15
- Update global max: ans = max(6, 15) = 15
- Return:
(1, 1, 5, 15)
Result: The maximum sum BST is the entire tree with sum = 15
Now let's consider a tree where not all subtrees are BSTs:
5 / \ 3 8 / 7
Processing node 7: Valid BST, sum = 7, ans = 7
Processing node 3:
- Left child (node 7) returned:
(1, 7, 7, 7)
- Right child (null) returned:
(1, inf, -inf, 0)
- Check BST validity:
- Both children are BSTs ✓
- Left max (7) < current (3) ✗ (FAILS!)
- This subtree is NOT a BST
- Return:
(0, 0, 0, 0)
Processing node 8: Valid BST, sum = 8, ans = max(7, 8) = 8
Processing root node 5:
- Left child (node 3) returned:
(0, 0, 0, 0)
(not a BST) - Right child (node 8) returned:
(1, 8, 8, 8)
- Check BST validity:
- Left child is not a BST ✗ (FAILS!)
- This subtree is NOT a BST
- Return:
(0, 0, 0, 0)
Result: The maximum sum BST is the single node 8 with sum = 8
This walkthrough demonstrates how the algorithm:
- Uses bottom-up traversal to gather information from children first
- Validates BST properties using min/max values from subtrees
- Tracks the maximum sum across all valid BST subtrees
- Handles cases where parent trees aren't BSTs even if some children are
Solution Implementation
1# Definition for a binary tree node.
2# class TreeNode:
3# def __init__(self, val=0, left=None, right=None):
4# self.val = val
5# self.left = left
6# self.right = right
7
8class Solution:
9 def maxSumBST(self, root: Optional[TreeNode]) -> int:
10 """
11 Find the maximum sum of all keys of any sub-tree which is also a Binary Search Tree (BST).
12
13 Args:
14 root: Root node of the binary tree
15
16 Returns:
17 Maximum sum of any BST subtree in the tree
18 """
19
20 def dfs(node: Optional[TreeNode]) -> tuple[int, int, int, int]:
21 """
22 Perform DFS to check if subtree is BST and calculate sum.
23
24 Args:
25 node: Current node being processed
26
27 Returns:
28 Tuple of (is_bst, min_value, max_value, sum_of_subtree)
29 - is_bst: 1 if subtree is valid BST, 0 otherwise
30 - min_value: Minimum value in the subtree
31 - max_value: Maximum value in the subtree
32 - sum_of_subtree: Sum of all nodes in the subtree
33 """
34 # Base case: empty node is a valid BST
35 if node is None:
36 return 1, float('inf'), float('-inf'), 0
37
38 # Recursively process left and right subtrees
39 left_is_bst, left_min, left_max, left_sum = dfs(node.left)
40 right_is_bst, right_min, right_max, right_sum = dfs(node.right)
41
42 # Check if current subtree forms a valid BST
43 # Conditions: both subtrees are BSTs and current node value is in valid range
44 if left_is_bst and right_is_bst and left_max < node.val < right_min:
45 # Calculate sum of current BST subtree
46 current_sum = left_sum + right_sum + node.val
47
48 # Update global maximum sum
49 nonlocal max_sum
50 max_sum = max(max_sum, current_sum)
51
52 # Return BST properties for current subtree
53 return (1,
54 min(left_min, node.val), # Minimum value in subtree
55 max(right_max, node.val), # Maximum value in subtree
56 current_sum) # Sum of subtree
57
58 # Current subtree is not a valid BST
59 return 0, 0, 0, 0
60
61 # Initialize maximum sum to 0 (empty BST has sum 0)
62 max_sum = 0
63
64 # Start DFS traversal from root
65 dfs(root)
66
67 return max_sum
68
1/**
2 * Definition for a binary tree node.
3 * public class TreeNode {
4 * int val;
5 * TreeNode left;
6 * TreeNode right;
7 * TreeNode() {}
8 * TreeNode(int val) { this.val = val; }
9 * TreeNode(int val, TreeNode left, TreeNode right) {
10 * this.val = val;
11 * this.left = left;
12 * this.right = right;
13 * }
14 * }
15 */
16class Solution {
17 // Global variable to track the maximum sum of any BST subtree
18 private int maxSum;
19
20 // Constant representing infinity for min/max comparisons
21 private static final int INFINITY = 1 << 30;
22
23 /**
24 * Finds the maximum sum of all keys in any Binary Search Tree (BST) subtree.
25 *
26 * @param root The root of the binary tree
27 * @return The maximum sum of keys in any BST subtree
28 */
29 public int maxSumBST(TreeNode root) {
30 maxSum = 0;
31 findMaxSumBST(root);
32 return maxSum;
33 }
34
35 /**
36 * Performs a post-order traversal to check if each subtree is a valid BST
37 * and calculates the sum of its nodes.
38 *
39 * @param node The current node being processed
40 * @return An array containing:
41 * [0] - isBST: 1 if the subtree is a valid BST, 0 otherwise
42 * [1] - minValue: Minimum value in the subtree
43 * [2] - maxValue: Maximum value in the subtree
44 * [3] - sum: Sum of all nodes in the subtree
45 */
46 private int[] findMaxSumBST(TreeNode node) {
47 // Base case: empty subtree is a valid BST
48 if (node == null) {
49 // Return: valid BST, min=infinity, max=-infinity, sum=0
50 return new int[] {1, INFINITY, -INFINITY, 0};
51 }
52
53 // Recursively process left and right subtrees
54 int[] leftResult = findMaxSumBST(node.left);
55 int[] rightResult = findMaxSumBST(node.right);
56
57 int currentValue = node.val;
58
59 // Check if current subtree forms a valid BST:
60 // 1. Both left and right subtrees must be valid BSTs
61 // 2. Maximum value in left subtree < current node value
62 // 3. Minimum value in right subtree > current node value
63 boolean isLeftBST = (leftResult[0] == 1);
64 boolean isRightBST = (rightResult[0] == 1);
65 boolean isValidBSTRange = (leftResult[2] < currentValue) && (rightResult[1] > currentValue);
66
67 if (isLeftBST && isRightBST && isValidBSTRange) {
68 // Current subtree is a valid BST
69 int currentSum = currentValue + leftResult[3] + rightResult[3];
70
71 // Update the global maximum sum if current BST has a larger sum
72 maxSum = Math.max(maxSum, currentSum);
73
74 // Return BST properties: valid BST, updated min/max values, and sum
75 int minValue = Math.min(leftResult[1], currentValue);
76 int maxValue = Math.max(rightResult[2], currentValue);
77 return new int[] {1, minValue, maxValue, currentSum};
78 }
79
80 // Current subtree is not a valid BST
81 // Return array with isBST = 0 (other values don't matter)
82 return new int[] {0, 0, 0, 0};
83 }
84}
85
1/**
2 * Definition for a binary tree node.
3 * struct TreeNode {
4 * int val;
5 * TreeNode *left;
6 * TreeNode *right;
7 * TreeNode() : val(0), left(nullptr), right(nullptr) {}
8 * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
9 * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
10 * };
11 */
12class Solution {
13public:
14 int maxSumBST(TreeNode* root) {
15 int maxSum = 0;
16 const int INF = 1 << 30; // Large value representing infinity
17
18 // DFS function returns an array with 4 elements:
19 // [0]: isBST (1 if subtree is BST, 0 otherwise)
20 // [1]: minimum value in subtree
21 // [2]: maximum value in subtree
22 // [3]: sum of all nodes in subtree
23 function<array<int, 4>(TreeNode*)> dfs = [&](TreeNode* node) -> array<int, 4> {
24 // Base case: empty tree is a valid BST
25 if (!node) {
26 return {1, INF, -INF, 0};
27 }
28
29 // Recursively process left and right subtrees
30 auto leftResult = dfs(node->left);
31 auto rightResult = dfs(node->right);
32
33 int currentVal = node->val;
34
35 // Check if current subtree forms a valid BST:
36 // 1. Both left and right subtrees must be BSTs
37 // 2. Maximum value in left subtree < current node value
38 // 3. Current node value < minimum value in right subtree
39 if (leftResult[0] && rightResult[0] &&
40 leftResult[2] < currentVal && currentVal < rightResult[1]) {
41
42 // Calculate sum of current BST subtree
43 int currentSum = leftResult[3] + rightResult[3] + currentVal;
44
45 // Update maximum sum found so far
46 maxSum = max(maxSum, currentSum);
47
48 // Return BST info for current subtree
49 return {
50 1, // isBST = true
51 min(leftResult[1], currentVal), // minimum value
52 max(rightResult[2], currentVal), // maximum value
53 currentSum // sum of subtree
54 };
55 }
56
57 // Current subtree is not a valid BST
58 return {0, 0, 0, 0}; // Only first element matters (isBST = false)
59 };
60
61 dfs(root);
62 return maxSum;
63 }
64};
65
1/**
2 * Definition for a binary tree node.
3 * class TreeNode {
4 * val: number
5 * left: TreeNode | null
6 * right: TreeNode | null
7 * constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) {
8 * this.val = (val===undefined ? 0 : val)
9 * this.left = (left===undefined ? null : left)
10 * this.right = (right===undefined ? null : right)
11 * }
12 * }
13 */
14
15/**
16 * Finds the maximum sum of all keys in any Binary Search Tree (BST) subtree
17 * @param root - The root node of the binary tree
18 * @returns The maximum sum of all keys in any BST subtree
19 */
20function maxSumBST(root: TreeNode | null): number {
21 // Large positive value representing infinity for comparison
22 const INFINITY: number = 1 << 30;
23
24 // Track the maximum sum found across all valid BST subtrees
25 let maxSum: number = 0;
26
27 /**
28 * Performs depth-first search to validate BST and calculate sums
29 * @param node - Current node being processed
30 * @returns Tuple of [isBST, minValue, maxValue, sum]
31 * - isBST: whether the subtree rooted at node is a valid BST
32 * - minValue: minimum value in the subtree
33 * - maxValue: maximum value in the subtree
34 * - sum: sum of all values in the subtree
35 */
36 const dfs = (node: TreeNode | null): [boolean, number, number, number] => {
37 // Base case: empty tree is a valid BST with sum 0
38 if (!node) {
39 return [true, INFINITY, -INFINITY, 0];
40 }
41
42 // Recursively process left subtree
43 const [isLeftBST, leftMin, leftMax, leftSum] = dfs(node.left);
44
45 // Recursively process right subtree
46 const [isRightBST, rightMin, rightMax, rightSum] = dfs(node.right);
47
48 // Check if current subtree forms a valid BST:
49 // 1. Both left and right subtrees must be valid BSTs
50 // 2. All values in left subtree must be less than current node's value
51 // 3. All values in right subtree must be greater than current node's value
52 if (isLeftBST && isRightBST && leftMax < node.val && node.val < rightMin) {
53 // Calculate the sum of the current BST subtree
54 const currentSum: number = leftSum + rightSum + node.val;
55
56 // Update the maximum sum if current BST has larger sum
57 maxSum = Math.max(maxSum, currentSum);
58
59 // Return BST properties for the current subtree
60 return [
61 true,
62 Math.min(leftMin, node.val), // Minimum value in current subtree
63 Math.max(rightMax, node.val), // Maximum value in current subtree
64 currentSum // Total sum of current subtree
65 ];
66 }
67
68 // Current subtree is not a valid BST
69 return [false, 0, 0, 0];
70 };
71
72 // Start DFS traversal from root
73 dfs(root);
74
75 return maxSum;
76}
77
Time and Space Complexity
Time Complexity: O(n)
The algorithm performs a depth-first search (DFS) traversal of the binary tree. Each node is visited exactly once during the traversal. At each node, the algorithm performs constant time operations:
- Comparing values (
lmx < root.val < rmi
) - Computing the sum (
ls + rs + root.val
) - Finding minimum and maximum values (
min(lmi, root.val)
,max(rmx, root.val)
) - Updating the answer (
max(ans, s)
)
Since we visit each of the n
nodes exactly once and perform O(1)
operations at each node, the overall time complexity is O(n)
.
Space Complexity: O(n)
The space complexity comes from two sources:
- Recursion call stack: In the worst case (skewed tree), the recursion depth can be
O(n)
, requiringO(n)
space on the call stack. - Return values: Each recursive call returns a tuple of 4 values, but these are
O(1)
space per call and don't accumulate.
In the best case (balanced tree), the recursion depth would be O(log n)
, but we must consider the worst case. Therefore, the space complexity is O(n)
.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Incorrect Infinity Values for Base Case
Pitfall: Using the wrong infinity values in the base case can break the BST validation logic. For example, if you return (1, -inf, inf, 0)
for a null node instead of (1, inf, -inf, 0)
, the BST property checks will fail.
Why it happens: The logic requires that:
- Any node value should be greater than the maximum of its left subtree
- Any node value should be less than the minimum of its right subtree
When a node has no left child, we need left_max = -inf
so that left_max < node.val
always passes. Similarly, when there's no right child, we need right_min = inf
so that node.val < right_min
always passes.
Solution:
# Correct base case
if node is None:
return 1, float('inf'), float('-inf'), 0
# NOT this:
# return 1, float('-inf'), float('inf'), 0 # Wrong!
2. Forgetting to Handle Single Node BSTs
Pitfall: Not updating the maximum sum when processing leaf nodes or single-node subtrees, which are valid BSTs by definition.
Why it happens: Developers might focus on complex subtrees and forget that a single node is also a valid BST that should be considered for the maximum sum.
Solution: The current implementation handles this correctly by updating max_sum
for every valid BST, including single nodes. The key is placing the update inside the BST validation block:
if left_is_bst and right_is_bst and left_max < node.val < right_min:
current_sum = left_sum + right_sum + node.val
max_sum = max(max_sum, current_sum) # Updates for ALL valid BSTs, including leaves
3. Using Weak BST Validation (≤ instead of <)
Pitfall: Using <=
or >=
in BST validation instead of strict inequalities, which would allow duplicate values and violate the BST property as defined in this problem.
Why it happens: Different BST definitions exist - some allow duplicates on one side. However, this problem specifically requires strict inequalities.
Solution:
# Correct: strict inequalities if left_is_bst and right_is_bst and left_max < node.val < right_min: # Wrong: would allow duplicates # if left_is_bst and right_is_bst and left_max <= node.val <= right_min:
4. Not Considering Empty Tree or Negative Values
Pitfall: Initializing max_sum
to a negative value or not handling trees with all negative values correctly.
Why it happens: Developers might assume all node values are positive or that there's always at least one valid BST with positive sum.
Solution: Initialize max_sum = 0
because:
- An empty subtree is a valid BST with sum 0
- If all BST subtrees have negative sums, we should return 0 (the empty BST)
- This handles the edge case where the entire tree has negative values
# Correct initialization max_sum = 0 # Not this: # max_sum = float('-inf') # Would return negative sum even when empty BST (sum=0) is better
Which of these properties could exist for a graph but not a tree?
Recommended Readings
Everything About Trees A tree is a type of graph data structure composed of nodes and edges Its main properties are It is acyclic doesn't contain any cycles There exists a path from the root to any node Has N 1 edges where N is the number of nodes in the tree and
https assets algo monster cover_photos dfs svg Depth First Search Prereqs Recursion Review problems recursion_intro Trees problems tree_intro With a solid understanding of recursion under our belts we are now ready to tackle one of the most useful techniques in coding interviews Depth First Search DFS As the name suggests
Binary Search Tree Intro Definition A Binary Search Tree or BST is a rooted binary tree with the value of each internal node being greater than all the values in the respective node's left subtree and less than the ones in its right subtree An empty tree is a BST since it satisfies the
Want a Structured Path to Master System Design Too? Don’t Miss This!