Maximum Level Sum of a Binary Tree
MedPrompt
Given the root of a binary tree, the level of its root is 1, the level of its children is 2, and so on.
Return the smallest level x such that the sum of all the values of nodes at level x is maximal.
Example 1:
Input: root = [1,7,0,7,-8,null,null]
Output: 2
Explanation:
Level 1 sum = 1.
Level 2 sum = 7 + 0 = 7.
Level 3 sum = 7 + -8 = -1.
So we return the level with the maximum sum which is level 2.Example 2:
Input: root = [989,null,10250,98693,-89388,null,null,null,-32127]
Output: 2
Constraints:
- The number of nodes in the tree is in the range
[1, 104]. -105 <= Node.val <= 105
Approaches
2 approaches with complexity analysis and trade-offs.
This approach uses a recursive Depth-First Search traversal to visit every node. We pass the current level as an argument in the recursive calls. A list is used to store the sum of node values for each level. After the traversal is complete, we iterate through this list to find the level with the maximum sum.
Algorithm
- Initialize an empty list,
levelSums, to store the sum of nodes for each level. - Define a recursive helper function
dfs(node, level). - In
dfs(node, level):- Base case: If
nodeis null, return. - If
levelis greater than the current size oflevelSums, addnode.valas a new entry. - Otherwise, update the sum at index
level - 1by addingnode.val. - Recursively call
dfsfor the left child withlevel + 1. - Recursively call
dfsfor the right child withlevel + 1.
- Base case: If
- Start the traversal by calling
dfs(root, 1). - After the traversal, iterate through
levelSumsto find the maximum sum. - Keep track of the index
iwhere the maximum sum is found. The first such index will correspond to the smallest level. - Return
i + 1as the result.
Walkthrough
This approach utilizes a Depth-First Search (DFS) traversal to compute the sum of nodes at each level. We use a recursive helper function that takes the current node and its level as arguments. An external data structure, like an ArrayList, is used to store the sum for each level. The index of the list corresponds to the level number (minus one). As the DFS traversal explores the tree, it populates this list with the sums. Once the traversal is complete, we perform a second pass over the list of sums to identify the maximum sum and the smallest level at which it occurs.
Here is the Java implementation:
/** * Definition for a binary tree node. * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode() {} * TreeNode(int val) { this.val = val; } * TreeNode(int val, TreeNode left, TreeNode right) { * this.val = val; * this.left = left; * this.right = right; * } * } */class Solution { private List<Integer> levelSums = new ArrayList<>(); public int maxLevelSum(TreeNode root) { dfs(root, 1); int maxSum = Integer.MIN_VALUE; int maxLevel = 0; for (int i = 0; i < levelSums.size(); i++) { if (levelSums.get(i) > maxSum) { maxSum = levelSums.get(i); maxLevel = i + 1; } } return maxLevel; } private void dfs(TreeNode node, int level) { if (node == null) { return; } if (level > levelSums.size()) { levelSums.add(node.val); } else { levelSums.set(level - 1, levelSums.get(level - 1) + node.val); } dfs(node.left, level + 1); dfs(node.right, level + 1); }}Complexity
Time
O(N), where N is the number of nodes in the tree. The DFS traversal takes O(N) time as it visits each node once. The subsequent loop to find the maximum sum takes O(H) time, where H is the height of the tree. Thus, the total time is O(N + H), which simplifies to O(N).
Space
O(H), where H is the height of the tree. This space is used by the recursion stack and the `levelSums` list, both of which can grow up to the height of the tree. In the worst case of a skewed tree, H can be N, making the space complexity O(N).
Trade-offs
Pros
Conceptually simple if you are very familiar with DFS and recursion.
Cons
Requires two passes over the data: one to traverse the tree and populate the sums, and a second to iterate through the sums to find the maximum.
Less intuitive for a level-by-level problem compared to BFS.
Space complexity can be O(N) for skewed trees, which is worse than BFS for that specific case.
This approach uses Breadth-First Search (BFS), which is the natural way to traverse a tree level by level. We use a queue to process nodes. In each iteration of the main loop, we process all nodes of a single level, calculate their sum, and compare it with the maximum sum found so far.
Algorithm
- Handle the edge case where the
rootis null. - Initialize a
Queueand add therootnode. - Initialize variables:
maxSumto a very small number,resultLevel = 1, andcurrentLevel = 1. - Begin a loop that continues as long as the queue is not empty.
- Inside the loop, first determine the number of nodes at the current level by getting the queue's size (
levelSize). - Initialize
currentLevelSum = 0. - Start an inner loop that runs
levelSizetimes to process all nodes of the current level:- Dequeue a node from the queue.
- Add the node's value to
currentLevelSum. - Enqueue the node's non-null left and right children.
- After the inner loop, compare
currentLevelSumwithmaxSum. If it's greater, updatemaxSumand setresultLeveltocurrentLevel. - Increment
currentLevelfor the next iteration. - After the main loop finishes, return
resultLevel.
Walkthrough
This approach uses Breadth-First Search (BFS), which is a natural fit for problems involving tree levels. By using a queue, we can process the tree one level at a time. We iterate through the levels, and for each level, we calculate the sum of its node values. We maintain a variable to track the maximum sum seen so far and the level at which it occurred. Since we process levels in increasing order (1, 2, 3, ...), the first time we find a maximum sum, we record its level. Any subsequent levels with the same maximum sum will be ignored, automatically satisfying the condition to return the smallest level. This method efficiently finds the result in a single pass over the tree.
Here is the Java implementation:
/** * Definition for a binary tree node. * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode() {} * TreeNode(int val) { this.val = val; } * TreeNode(int val, TreeNode left, TreeNode right) { * this.val = val; * this.left = left; * this.right = right; * } * } */class Solution { public int maxLevelSum(TreeNode root) { if (root == null) { return 0; } Queue<TreeNode> queue = new LinkedList<>(); queue.offer(root); int maxSum = Integer.MIN_VALUE; int resultLevel = 0; int currentLevel = 1; while (!queue.isEmpty()) { int levelSize = queue.size(); int currentLevelSum = 0; for (int i = 0; i < levelSize; i++) { TreeNode node = queue.poll(); currentLevelSum += node.val; if (node.left != null) { queue.offer(node.left); } if (node.right != null) { queue.offer(node.right); } } if (currentLevelSum > maxSum) { maxSum = currentLevelSum; resultLevel = currentLevel; } currentLevel++; } return resultLevel; }}Complexity
Time
O(N), where N is the number of nodes. Each node is enqueued and dequeued exactly once.
Space
O(W), where W is the maximum width of the binary tree. The space is dominated by the queue, which holds at most all nodes of the widest level. In the worst case (a complete binary tree), W can be up to N/2, making the space complexity O(N).
Trade-offs
Pros
Very intuitive for a level-order problem.
Calculates the sum and finds the maximum in a single pass.
Naturally finds the smallest level in case of ties because levels are processed in order.
More space-efficient than DFS for tall, skinny trees.
Cons
Can be less space-efficient than DFS for short, wide trees (e.g., complete binary trees where space is O(N) vs DFS's O(log N)).
Solutions
Solution
/** * Definition for a binary tree node. * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode() {} * TreeNode(int val) { this.val = val; } * TreeNode(int val, TreeNode left, TreeNode right) { * this.val = val; * this.left = left; * this.right = right; * } * } */ class Solution { public int maxLevelSum ( TreeNode root ) { Deque < TreeNode > q = new ArrayDeque <>(); q . offer ( root ); int mx = Integer . MIN_VALUE ; int i = 0 ; int ans = 0 ; while (! q . isEmpty ()) { ++ i ; int s = 0 ; for ( int n = q . size (); n > 0 ; -- n ) { TreeNode node = q . pollFirst (); s += node . val ; if ( node . left != null ) { q . offer ( node . left ); } if ( node . right != null ) { q . offer ( node . right ); } } if ( mx < s ) { mx = s ; ans = i ; } } return ans ; } }/** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode() : val(0), left(nullptr), right(nullptr) {} * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} * }; */ class Solution { public: int maxLevelSum ( TreeNode * root ) { queue < TreeNode *> q { { root } }; int mx = INT_MIN ; int ans = 0 ; int i = 0 ; while ( ! q . empty ()) { ++ i ; int s = 0 ; for ( int n = q . size (); n ; -- n ) { root = q . front (); q . pop (); s += root -> val ; if ( root -> left ) q . push ( root -> left ); if ( root -> right ) q . push ( root -> right ); } if ( mx < s ) mx = s , ans = i ; } return ans ; } };# 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 maxLevelSum ( self , root : Optional [ TreeNode ]) -> int : q = deque ([ root ]) mx = - inf i = 0 while q : i += 1 s = 0 for _ in range ( len ( q )): node = q . popleft () s += node . val if node . left : q . append ( node . left ) if node . right : q . append ( node . right ) if mx < s : mx = s ans = i return ansVideo walkthrough
Newsletter
One sharp idea, every week
System design and interview prep — short enough to finish.
No spam. Unsubscribe anytime.
Practice
Same difficulty — related problems to reinforce the pattern.