Facebook Pixel

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:

  1. Calculates the sum of all node values at that level
  2. Keeps track of the maximum sum seen so far (mx)
  3. Updates the answer (ans) whenever a new maximum is found
  4. Returns the level number with the maximum sum

The key variables in the code are:

  • q: A queue (deque) for BFS traversal
  • mx: Tracks the maximum sum found so far (initialized to negative infinity)
  • i: The current level number
  • s: The sum of values at the current level
  • ans: 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:

  1. Level-by-level processing: The problem requires calculating sums at each level, which naturally aligns with BFS's level-order traversal pattern.

  2. 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.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

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:

  1. 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.

  2. 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.

  3. 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:

  1. Initialize the queue: Start with the root node in the queue

    q = deque([root])
  2. Set up tracking variables:

    • mx = -inf: Initialize maximum sum to negative infinity to handle negative values
    • i = 0: Level counter starting at 0 (will be incremented to 1 in first iteration)
  3. Process each level: While the queue is not empty:

    while q:
        i += 1  # Increment level number
        s = 0   # Initialize sum for current level
  4. 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, since len(q) at the start of each iteration represents the number of nodes at that level.

  5. 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)
  6. 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.

  7. 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 Evaluator

Example 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]
  • Check if new maximum: mx (-inf) < s (1)? Yes!
    • Update: mx = 1, ans = 1

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]
  • Process node 0:
    • Add to sum: s = 7 + 0 = 7
    • Add children to queue: q = [7, -8, 6]
  • Check if new maximum: mx (1) < s (7)? Yes!
    • Update: mx = 7, ans = 2

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
  • Process node -8:
    • Add to sum: s = 7 + (-8) = -1
    • No children to add
  • Process node 6:
    • Add to sum: s = -1 + 6 = 5
    • No children to add
  • 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.

Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

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

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More