1161. Maximum Level Sum of a Binary Tree
Problem Description
You are given the root of a binary tree where each level is numbered starting from 1 (the root is at level 1, its children are at level 2, and so on).
Your task is to find the level that has the maximum sum of node values. If multiple levels have the same maximum sum, return the smallest level number.
For example, if you have a binary tree where:
- Level 1 has nodes with sum 10
- Level 2 has nodes with sum 25
- Level 3 has nodes with sum 15
- Level 4 has nodes with sum 25
You would return level 2, since levels 2 and 4 both have the maximum sum of 25, but level 2 is smaller.
The solution uses a breadth-first search (BFS) approach with a queue to traverse the tree level by level. For each level, it:
- Calculates the sum of all node values at that level
- Keeps track of the maximum sum seen so far (
mx
) - Updates the answer (
ans
) whenever a new maximum is found - Returns the level number with the maximum sum
The key variables in the code are:
q
: A queue (deque) for BFS traversalmx
: Tracks the maximum sum found so far (initialized to negative infinity)i
: The current level numbers
: The sum of values at the current levelans
: The level number with the maximum sum
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
- Following the flowchart, when we have a tree structure, the flowchart directly points us to DFS as the recommended approach.
Conclusion: The flowchart suggests using DFS (Depth-First Search) for this tree problem.
However, it's worth noting that while DFS is a valid approach for this problem, the provided solution actually uses BFS (Breadth-First Search) instead. This is because:
-
Level-by-level processing: The problem requires calculating sums at each level, which naturally aligns with BFS's level-order traversal pattern.
-
DFS alternative: While DFS can solve this problem (by tracking the level during recursion and accumulating sums in a dictionary/array), BFS is more intuitive since we process all nodes at the same level together.
The DFS approach would involve:
- Recursively traversing the tree while tracking the current level
- Maintaining a data structure to store the sum for each level
- After traversal, finding the level with the maximum sum
The BFS approach (as shown in the solution) is more straightforward for this particular problem since it naturally processes nodes level by level, making it easier to calculate level sums directly.
Intuition
When we need to find something specific about each level of a tree, we should think about how to process the tree level by level. The key insight is that we need to calculate the sum of all nodes at each level and keep track of which level has the maximum sum.
While the flowchart suggests DFS for tree problems, this particular problem is actually better suited for BFS because:
-
Natural level grouping: BFS processes all nodes at the same level before moving to the next level. This makes it trivial to calculate the sum of each level - we just add up all the nodes currently in our queue before moving to the next level.
-
Sequential level numbering: Since BFS processes levels in order (1, 2, 3, ...), we can easily track the current level number with a simple counter that increments after processing each level.
-
Single pass efficiency: With BFS, we can calculate each level's sum in a single pass through the tree. We don't need to store intermediate results or make multiple traversals.
The approach becomes clear when we visualize the process:
- Start with the root at level 1
- Process all nodes at the current level, calculating their sum
- While processing, add their children to the queue for the next level
- Compare the current level's sum with the maximum found so far
- If it's greater, update both the maximum sum and the answer (level number)
- Move to the next level and repeat
The trick of only updating the answer when we find a strictly greater sum (if mx < s
) ensures that if multiple levels have the same maximum sum, we automatically keep the smallest level number.
Learn more about Tree, Depth-First Search, Breadth-First Search and Binary Tree patterns.
Solution Approach
The solution implements a BFS (Breadth-First Search) traversal using a queue to process the tree level by level. Here's how the implementation works:
Data Structures Used:
deque
: A double-ended queue for efficient BFS traversal- Variables to track the maximum sum (
mx
), current level (i
), and the answer level (ans
)
Algorithm Steps:
-
Initialize the queue: Start with the root node in the queue
q = deque([root])
-
Set up tracking variables:
mx = -inf
: Initialize maximum sum to negative infinity to handle negative valuesi = 0
: Level counter starting at 0 (will be incremented to 1 in first iteration)
-
Process each level: While the queue is not empty:
while q: i += 1 # Increment level number s = 0 # Initialize sum for current level
-
Calculate level sum: Process all nodes at the current level
for _ in range(len(q)): node = q.popleft() s += node.val
The key here is using
range(len(q))
to process exactly the nodes that belong to the current level, sincelen(q)
at the start of each iteration represents the number of nodes at that level. -
Add children for next level: While processing each node, add its children to the queue
if node.left: q.append(node.left) if node.right: q.append(node.right)
-
Update maximum and answer: After calculating the sum for the current level, check if it's the new maximum
if mx < s: mx = s ans = i
Using strict inequality (
<
) ensures that when multiple levels have the same maximum sum, we keep the smallest level number. -
Return the result: After processing all levels, return the level with the maximum sum
return ans
Time Complexity: O(n)
where n is the number of nodes, as we visit each node exactly once.
Space Complexity: O(w)
where w is the maximum width of the tree, which is the maximum number of nodes at any level (this represents the maximum queue size).
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 concrete example to illustrate the solution approach.
Consider this binary tree:
1 / \ 7 0 / \ \ 7 -8 6
We need to find the level with the maximum sum.
Initial Setup:
- Queue
q = [1]
(contains root) mx = -infinity
(maximum sum so far)i = 0
(current level, will increment to 1)ans = 0
(level with maximum sum)
Level 1 Processing:
- Increment level:
i = 1
- Current queue size: 1 node to process
- Initialize level sum:
s = 0
- Process node 1:
- Add to sum:
s = 0 + 1 = 1
- Add children to queue:
q = [7, 0]
- Add to sum:
- Check if new maximum:
mx (-inf) < s (1)
? Yes!- Update:
mx = 1
,ans = 1
- Update:
Level 2 Processing:
- Increment level:
i = 2
- Current queue size: 2 nodes to process
- Initialize level sum:
s = 0
- Process node 7:
- Add to sum:
s = 0 + 7 = 7
- Add children to queue:
q = [0, 7, -8]
- Add to sum:
- Process node 0:
- Add to sum:
s = 7 + 0 = 7
- Add children to queue:
q = [7, -8, 6]
- Add to sum:
- Check if new maximum:
mx (1) < s (7)
? Yes!- Update:
mx = 7
,ans = 2
- Update:
Level 3 Processing:
- Increment level:
i = 3
- Current queue size: 3 nodes to process
- Initialize level sum:
s = 0
- Process node 7:
- Add to sum:
s = 0 + 7 = 7
- No children to add
- Add to sum:
- Process node -8:
- Add to sum:
s = 7 + (-8) = -1
- No children to add
- Add to sum:
- Process node 6:
- Add to sum:
s = -1 + 6 = 5
- No children to add
- Add to sum:
- Check if new maximum:
mx (7) < s (5)
? No!- No update needed
Final Result:
- Queue is empty, traversal complete
- Return
ans = 2
The level with the maximum sum is level 2, with a sum of 7.
Notice how the algorithm naturally handles the level-by-level processing through the queue mechanism, and the strict inequality check ensures we keep the smallest level number when ties occur.
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
8from collections import deque
9from typing import Optional
10from math import inf
11
12class Solution:
13 def maxLevelSum(self, root: Optional[TreeNode]) -> int:
14 """
15 Find the level with the maximum sum in a binary tree.
16 Uses BFS to traverse the tree level by level.
17
18 Args:
19 root: The root node of the binary tree
20
21 Returns:
22 The 1-indexed level number with the maximum sum
23 """
24 # Initialize queue for BFS with root node
25 queue = deque([root])
26
27 # Track maximum sum found so far
28 max_sum = -inf
29
30 # Current level number (1-indexed)
31 current_level = 0
32
33 # Result level with maximum sum
34 result_level = 0
35
36 # Process each level of the tree
37 while queue:
38 current_level += 1
39
40 # Calculate sum for current level
41 level_sum = 0
42
43 # Process all nodes at current level
44 level_size = len(queue)
45 for _ in range(level_size):
46 node = queue.popleft()
47 level_sum += node.val
48
49 # Add children to queue for next level
50 if node.left:
51 queue.append(node.left)
52 if node.right:
53 queue.append(node.right)
54
55 # Update maximum sum and corresponding level if needed
56 if max_sum < level_sum:
57 max_sum = level_sum
58 result_level = current_level
59
60 return result_level
61
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 /**
18 * Finds the level with the maximum sum in a binary tree.
19 * Uses BFS (level-order traversal) to calculate sum for each level.
20 *
21 * @param root The root node of the binary tree
22 * @return The level number (1-indexed) with the maximum sum
23 */
24 public int maxLevelSum(TreeNode root) {
25 // Queue for BFS traversal
26 Deque<TreeNode> queue = new ArrayDeque<>();
27 queue.offer(root);
28
29 // Track the maximum sum found so far
30 int maxSum = Integer.MIN_VALUE;
31
32 // Current level number (1-indexed)
33 int currentLevel = 0;
34
35 // Level with the maximum sum
36 int resultLevel = 0;
37
38 // Process each level of the tree
39 while (!queue.isEmpty()) {
40 currentLevel++;
41
42 // Calculate sum for current level
43 int levelSum = 0;
44 int nodesInCurrentLevel = queue.size();
45
46 // Process all nodes at the current level
47 for (int i = 0; i < nodesInCurrentLevel; i++) {
48 TreeNode currentNode = queue.pollFirst();
49 levelSum += currentNode.val;
50
51 // Add left child to queue for next level
52 if (currentNode.left != null) {
53 queue.offer(currentNode.left);
54 }
55
56 // Add right child to queue for next level
57 if (currentNode.right != null) {
58 queue.offer(currentNode.right);
59 }
60 }
61
62 // Update maximum sum and corresponding level if current level has higher sum
63 if (levelSum > maxSum) {
64 maxSum = levelSum;
65 resultLevel = currentLevel;
66 }
67 }
68
69 return resultLevel;
70 }
71}
72
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 maxLevelSum(TreeNode* root) {
15 // Initialize queue for BFS traversal with root node
16 queue<TreeNode*> bfsQueue;
17 bfsQueue.push(root);
18
19 // Track maximum sum found so far
20 int maxSum = INT_MIN;
21
22 // Track the level with maximum sum
23 int levelWithMaxSum = 0;
24
25 // Current level number (1-indexed)
26 int currentLevel = 0;
27
28 // Process tree level by level
29 while (!bfsQueue.empty()) {
30 currentLevel++;
31
32 // Calculate sum for current level
33 int currentLevelSum = 0;
34 int nodesInCurrentLevel = bfsQueue.size();
35
36 // Process all nodes at current level
37 for (int i = 0; i < nodesInCurrentLevel; i++) {
38 TreeNode* currentNode = bfsQueue.front();
39 bfsQueue.pop();
40
41 // Add current node's value to level sum
42 currentLevelSum += currentNode->val;
43
44 // Add children to queue for next level processing
45 if (currentNode->left != nullptr) {
46 bfsQueue.push(currentNode->left);
47 }
48 if (currentNode->right != nullptr) {
49 bfsQueue.push(currentNode->right);
50 }
51 }
52
53 // Update maximum sum and corresponding level if current level has higher sum
54 if (currentLevelSum > maxSum) {
55 maxSum = currentLevelSum;
56 levelWithMaxSum = currentLevel;
57 }
58 }
59
60 return levelWithMaxSum;
61 }
62};
63
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 level with the maximum sum in a binary tree
17 * Uses BFS (Breadth-First Search) to traverse the tree level by level
18 * @param root - The root node of the binary tree
19 * @returns The level number (1-indexed) with the maximum sum
20 */
21function maxLevelSum(root: TreeNode | null): number {
22 // Initialize queue for BFS traversal
23 const queue: TreeNode[] = [root!];
24
25 // Track the result level (1-indexed)
26 let resultLevel: number = 1;
27
28 // Track the maximum sum found so far
29 let maxSum: number = -Infinity;
30
31 // Track the current level number (1-indexed)
32 let currentLevel: number = 1;
33
34 // Process each level of the tree
35 while (queue.length > 0) {
36 // Get the number of nodes at the current level
37 const nodesAtCurrentLevel: number = queue.length;
38
39 // Calculate sum for the current level
40 let currentLevelSum: number = 0;
41
42 // Process all nodes at the current level
43 for (let i = 0; i < nodesAtCurrentLevel; i++) {
44 // Dequeue the next node
45 const currentNode: TreeNode = queue.shift()!;
46
47 // Add current node's value to level sum
48 currentLevelSum += currentNode.val;
49
50 // Add children to queue for next level processing
51 if (currentNode.left) {
52 queue.push(currentNode.left);
53 }
54 if (currentNode.right) {
55 queue.push(currentNode.right);
56 }
57 }
58
59 // Update maximum sum and corresponding level if current level has higher sum
60 if (currentLevelSum > maxSum) {
61 maxSum = currentLevelSum;
62 resultLevel = currentLevel;
63 }
64
65 // Move to the next level
66 currentLevel++;
67 }
68
69 return resultLevel;
70}
71
Time and Space Complexity
Time Complexity: O(n)
The algorithm uses BFS (Breadth-First Search) to traverse the binary tree level by level. Each node in the tree is visited exactly once - it's added to the queue once and processed once when it's dequeued. For each node, we perform constant-time operations: adding its value to the sum, checking for left/right children, and appending children to the queue. Since we visit all n
nodes exactly once and perform O(1)
operations for each node, the total time complexity is O(n)
.
Space Complexity: O(n)
The space complexity is determined by the maximum size of the queue during the traversal. In the worst case, the queue needs to store all nodes at the widest level of the tree. For a complete binary tree, the last level can contain up to n/2
nodes (approximately half of all nodes in the tree), making the space complexity O(n)
. Additionally, we use a few variables (mx
, i
, s
, ans
) which require O(1)
space, but this doesn't affect the overall space complexity which remains O(n)
.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Off-by-One Error with Level Numbering
A common mistake is incorrectly handling the 1-indexed level numbering. Developers might initialize the level counter at 1 and increment it at the wrong time, leading to incorrect level assignments.
Incorrect approach:
level = 1 while queue: # Process level... level += 1 # Incrementing after processing might assign wrong level
Correct approach:
level = 0 while queue: level += 1 # Increment first to get 1-indexed levels # Process level...
2. Not Preserving Level Boundaries in BFS
A critical mistake is not properly isolating nodes belonging to each level. If you don't capture the queue size at the start of each level iteration, you'll mix nodes from different levels.
Incorrect approach:
while queue: node = queue.popleft() level_sum += node.val # This mixes all levels together! if node.left: queue.append(node.left) if node.right: queue.append(node.right)
Correct approach:
while queue:
level_size = len(queue) # Capture current level size
level_sum = 0
for _ in range(level_size): # Process exactly this many nodes
node = queue.popleft()
level_sum += node.val
# Add children...
3. Incorrect Initialization of Maximum Sum
Initializing max_sum
to 0 instead of negative infinity can cause issues when all node values in the tree are negative.
Incorrect approach:
max_sum = 0 # Fails for trees with all negative values
Correct approach:
max_sum = -inf # Handles any possible node values
4. Using <=
Instead of <
for Maximum Comparison
Using <=
when updating the maximum would return the largest level number when multiple levels have the same maximum sum, violating the requirement to return the smallest level.
Incorrect approach:
if max_sum <= level_sum: # Wrong! Returns last level with max sum max_sum = level_sum result_level = current_level
Correct approach:
if max_sum < level_sum: # Correct! Keeps first level with max sum max_sum = level_sum result_level = current_level
5. Not Handling Edge Cases
Forgetting to handle edge cases like a single-node tree or not checking if nodes exist before adding to queue.
Solution: Always check if child nodes exist before adding them to the queue, and ensure the algorithm works correctly for a tree with just the root node.
What's the output of running the following function using input 56
?
1KEYBOARD = {
2 '2': 'abc',
3 '3': 'def',
4 '4': 'ghi',
5 '5': 'jkl',
6 '6': 'mno',
7 '7': 'pqrs',
8 '8': 'tuv',
9 '9': 'wxyz',
10}
11
12def letter_combinations_of_phone_number(digits):
13 def dfs(path, res):
14 if len(path) == len(digits):
15 res.append(''.join(path))
16 return
17
18 next_number = digits[len(path)]
19 for letter in KEYBOARD[next_number]:
20 path.append(letter)
21 dfs(path, res)
22 path.pop()
23
24 res = []
25 dfs([], res)
26 return res
27
1private static final Map<Character, char[]> KEYBOARD = Map.of(
2 '2', "abc".toCharArray(),
3 '3', "def".toCharArray(),
4 '4', "ghi".toCharArray(),
5 '5', "jkl".toCharArray(),
6 '6', "mno".toCharArray(),
7 '7', "pqrs".toCharArray(),
8 '8', "tuv".toCharArray(),
9 '9', "wxyz".toCharArray()
10);
11
12public static List<String> letterCombinationsOfPhoneNumber(String digits) {
13 List<String> res = new ArrayList<>();
14 dfs(new StringBuilder(), res, digits.toCharArray());
15 return res;
16}
17
18private static void dfs(StringBuilder path, List<String> res, char[] digits) {
19 if (path.length() == digits.length) {
20 res.add(path.toString());
21 return;
22 }
23 char next_digit = digits[path.length()];
24 for (char letter : KEYBOARD.get(next_digit)) {
25 path.append(letter);
26 dfs(path, res, digits);
27 path.deleteCharAt(path.length() - 1);
28 }
29}
30
1const KEYBOARD = {
2 '2': 'abc',
3 '3': 'def',
4 '4': 'ghi',
5 '5': 'jkl',
6 '6': 'mno',
7 '7': 'pqrs',
8 '8': 'tuv',
9 '9': 'wxyz',
10}
11
12function letter_combinations_of_phone_number(digits) {
13 let res = [];
14 dfs(digits, [], res);
15 return res;
16}
17
18function dfs(digits, path, res) {
19 if (path.length === digits.length) {
20 res.push(path.join(''));
21 return;
22 }
23 let next_number = digits.charAt(path.length);
24 for (let letter of KEYBOARD[next_number]) {
25 path.push(letter);
26 dfs(digits, path, res);
27 path.pop();
28 }
29}
30
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
https assets algo monster cover_photos bfs svg Breadth First Search on Trees Hopefully by this time you've drunk enough DFS Kool Aid to understand its immense power and seen enough visualization to create a call stack in your mind Now let me introduce the companion spell Breadth First Search BFS
Want a Structured Path to Master System Design Too? Don’t Miss This!