LeetCode 1161 - Maximum Level Sum of a Binary Tree
In this problem, we are given the root node of a binary tree. Every node belongs to a specific level in the tree. The root is at level 1, its direct children are at level 2, their children are at level 3, and so on.
Difficulty: 🟡 Medium
Topics: Tree, Depth-First Search, Breadth-First Search, Binary Tree
Solution
Problem Understanding
In this problem, we are given the root node of a binary tree. Every node belongs to a specific level in the tree. The root is at level 1, its direct children are at level 2, their children are at level 3, and so on.
Our goal is to compute the sum of node values at every level and return the level number whose sum is the largest. If multiple levels have the same maximum sum, we must return the smallest level number among them.
The input is a standard binary tree structure where each node contains:
- An integer value
- A reference to a left child
- A reference to a right child
The output is a single integer representing the level with the highest sum.
The constraints tell us several important things:
- The tree can contain up to
10^4nodes, so the solution must be efficient. - Node values may be negative, which means we cannot assume sums are always increasing.
- Since the tree is not guaranteed to be balanced, the height could be as large as the number of nodes.
A naive solution that repeatedly traverses the tree level by level could become inefficient. We need a method that visits every node only once.
There are several important edge cases to consider:
- A tree containing only one node
- Trees where all values are negative
- Multiple levels having the same maximum sum
- Highly unbalanced trees that resemble linked lists
- Sparse trees with many
nullchildren
The problem guarantees that at least one node exists, so we never need to handle an empty tree.
Approaches
Brute Force Approach
A brute force strategy would first compute the height of the tree. Then, for every level from 1 to height, we would perform a traversal to calculate the sum of nodes at that specific depth.
For example:
- Traverse the tree to compute all nodes at level
1 - Traverse again for level
2 - Traverse again for level
3 - Continue until the deepest level
This works because every level sum is computed correctly. However, it is inefficient because the tree is repeatedly traversed.
If the tree height is h and there are n nodes, this approach may take O(n * h) time. In the worst case of a skewed tree, h = n, leading to O(n^2) complexity.
Optimal Approach
The key observation is that we can compute level sums during a single traversal of the tree.
Breadth-First Search, also called level-order traversal, is ideal because it naturally processes nodes one level at a time.
Using a queue:
- Start with the root node
- Process all nodes currently in the queue, these belong to the same level
- Compute their total sum
- Add their children into the queue
- Move to the next level
This guarantees that every node is visited exactly once.
Because we process one level at a time, it becomes easy to track:
- The current level number
- The sum of that level
- The maximum sum seen so far
- The corresponding level number
This reduces the time complexity to O(n).
| Approach | Time Complexity | Space Complexity | Notes |
|---|---|---|---|
| Brute Force | O(n * h) | O(h) | Repeatedly traverses the tree for each level |
| Optimal | O(n) | O(w) | Single BFS traversal, where w is maximum tree width |
Algorithm Walkthrough
- Create a queue and insert the root node into it.
The queue allows us to process the tree level by level. This is exactly what Breadth-First Search is designed for. 2. Initialize tracking variables.
We maintain:
current_level, starting at1best_level, initially1max_sum, initially negative infinity
These variables help track which level currently has the largest sum. 3. Process the queue while it is not empty.
Each iteration of the loop handles one entire level of the tree. 4. Determine the number of nodes in the current level.
The queue length at the start of the iteration tells us exactly how many nodes belong to this level. 5. Compute the level sum.
Remove each node from the queue:
- Add its value to
level_sum - Push its left child into the queue if it exists
- Push its right child into the queue if it exists
- Compare the current level sum with the best sum seen so far.
If level_sum > max_sum:
- Update
max_sum - Update
best_level
We use strictly greater comparison so that ties automatically preserve the smallest level number. 7. Increment the level counter.
After processing all nodes in the current level, move to the next level.
8. Return best_level.
Once the traversal is complete, this variable contains the smallest level with the maximum sum.
Why it works
Breadth-First Search processes nodes level by level in exact order from top to bottom. At the start of each iteration, the queue contains only nodes belonging to the current level. Therefore, summing all nodes currently processed gives the exact sum for that level.
Since every node is visited exactly once and every level sum is compared against the global maximum, the algorithm correctly identifies the smallest level with the maximum sum.
Python Solution
from collections import deque
from typing import Optional
# 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:
queue = deque([root])
current_level = 1
best_level = 1
max_sum = float("-inf")
while queue:
level_size = len(queue)
level_sum = 0
for _ in range(level_size):
node = queue.popleft()
level_sum += node.val
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
if level_sum > max_sum:
max_sum = level_sum
best_level = current_level
current_level += 1
return best_level
The implementation begins by creating a queue containing the root node. The queue enables level-order traversal.
The variables current_level, best_level, and max_sum keep track of traversal progress and the best answer found so far.
Inside the main loop, level_size captures how many nodes belong to the current level. This is important because new child nodes are added during iteration, and we only want to process the current level in each pass.
The inner loop removes nodes one by one, adds their values to level_sum, and inserts any existing children into the queue for future processing.
After finishing the level, the algorithm compares the current sum against the maximum sum seen so far. If the current level is better, the answer variables are updated.
Finally, the level counter increases and the traversal continues until all nodes have been processed.
Go Solution
/**
* Definition for a binary tree node.
* type TreeNode struct {
* Val int
* Left *TreeNode
* Right *TreeNode
* }
*/
func maxLevelSum(root *TreeNode) int {
queue := []*TreeNode{root}
currentLevel := 1
bestLevel := 1
maxSum := -(1 << 60)
for len(queue) > 0 {
levelSize := len(queue)
levelSum := 0
for i := 0; i < levelSize; i++ {
node := queue[0]
queue = queue[1:]
levelSum += node.Val
if node.Left != nil {
queue = append(queue, node.Left)
}
if node.Right != nil {
queue = append(queue, node.Right)
}
}
if levelSum > maxSum {
maxSum = levelSum
bestLevel = currentLevel
}
currentLevel++
}
return bestLevel
}
The Go implementation follows the same BFS logic as the Python solution.
Instead of Python's deque, Go uses a slice as a queue. Removing the front element is done using:
queue = queue[1:]
Go uses nil checks for child nodes instead of truthy checks.
The variable maxSum is initialized to a very small negative number to correctly handle trees containing only negative values.
Since the constraints fit comfortably within Go's integer range, integer overflow is not a concern here.
Worked Examples
Example 1
Input:
root = [1,7,0,7,-8,null,null]
Tree structure:
1
/ \
7 0
/ \
7 -8
Step-by-step traversal
| Level | Queue Before Processing | Level Sum | Best Level | Max Sum |
|---|---|---|---|---|
| 1 | [1] | 1 | 1 | 1 |
| 2 | [7, 0] | 7 | 2 | 7 |
| 3 | [7, -8] | -1 | 2 | 7 |
Final answer:
2
Example 2
Input:
root = [989,null,10250,98693,-89388,null,null,null,-32127]
Tree structure:
989
\
10250
/ \
98693 -89388
/
-32127
Step-by-step traversal
| Level | Nodes | Level Sum |
|---|---|---|
| 1 | [989] | 989 |
| 2 | [10250] | 10250 |
| 3 | [98693, -89388] | 9305 |
| 4 | [-32127] | -32127 |
The maximum sum is 10250 at level 2.
Final answer:
2
Complexity Analysis
| Measure | Complexity | Explanation |
|---|---|---|
| Time | O(n) | Every node is visited exactly once |
| Space | O(w) | Queue stores at most one level at a time |
The algorithm performs a standard Breadth-First Search traversal. Each node enters and leaves the queue exactly once, producing linear time complexity.
The space usage depends on the maximum number of nodes stored simultaneously in the queue. In the worst case of a complete binary tree, this can be approximately half the nodes, giving O(w) space complexity where w is the maximum tree width.
Test Cases
from collections import deque
from typing import Optional
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:
queue = deque([root])
current_level = 1
best_level = 1
max_sum = float("-inf")
while queue:
level_size = len(queue)
level_sum = 0
for _ in range(level_size):
node = queue.popleft()
level_sum += node.val
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
if level_sum > max_sum:
max_sum = level_sum
best_level = current_level
current_level += 1
return best_level
solution = Solution()
# Example 1
root1 = TreeNode(
1,
TreeNode(7, TreeNode(7), TreeNode(-8)),
TreeNode(0)
)
assert solution.maxLevelSum(root1) == 2 # maximum sum at level 2
# Example 2
root2 = TreeNode(
989,
None,
TreeNode(
10250,
TreeNode(98693),
TreeNode(-89388, TreeNode(-32127), None)
)
)
assert solution.maxLevelSum(root2) == 2 # level 2 has largest sum
# Single node tree
root3 = TreeNode(5)
assert solution.maxLevelSum(root3) == 1 # only one level exists
# All negative values
root4 = TreeNode(
-1,
TreeNode(-2),
TreeNode(-3)
)
assert solution.maxLevelSum(root4) == 1 # -1 is greater than -5
# Tie between levels
root5 = TreeNode(
1,
TreeNode(2),
TreeNode(1)
)
assert solution.maxLevelSum(root5) == 1 # both levels sum to 3, choose smaller level
# Left-skewed tree
root6 = TreeNode(
1,
TreeNode(
2,
TreeNode(
3,
TreeNode(4)
)
)
)
assert solution.maxLevelSum(root6) == 4 # deepest level has largest sum
# Sparse tree
root7 = TreeNode(
10,
TreeNode(2, None, TreeNode(5)),
TreeNode(3)
)
assert solution.maxLevelSum(root7) == 3 # level 3 sum is 5
| Test | Why |
|---|---|
[1,7,0,7,-8,null,null] |
Validates the primary example |
[989,null,10250,98693,-89388,null,null,null,-32127] |
Tests sparse structure with negative values |
| Single node tree | Ensures minimal input works |
| All negative values | Confirms maximum among negative sums is handled correctly |
| Tie between levels | Verifies smallest level number is returned |
| Left-skewed tree | Tests highly unbalanced trees |
| Sparse tree | Ensures missing children do not affect traversal |
Edge Cases
One important edge case is a tree containing only a single node. In this scenario, the root is both the only node and the only level. A careless implementation might incorrectly initialize variables or fail to process the queue correctly. This solution handles the case naturally because the BFS loop runs once and returns level 1.
Another critical case involves trees where all node values are negative. Some implementations incorrectly initialize the maximum sum to 0, which would fail because every level sum would be smaller than zero. This implementation initializes max_sum to negative infinity, ensuring that negative sums are processed correctly.
A third important edge case is when multiple levels share the same maximum sum. The problem specifically requires returning the smallest level number. This implementation uses a strictly greater comparison:
if level_sum > max_sum:
Because equal sums do not overwrite the previous answer, the earliest level is preserved automatically.
Highly unbalanced trees are another potential source of bugs. In a skewed tree, the height can approach the number of nodes. Recursive DFS solutions could risk stack depth issues in some languages. The iterative BFS solution avoids recursion entirely and handles skewed trees safely.