Level Order Traversal

Traverse a binary tree level by level from left to right.

Language Selection

Choose your preferred programming language

Showing: Python

Level Order Traversal

Problem Statement

Given the root of a binary tree, return the level order traversal of its nodes’ values. (i.e., from left to right, level by level).

Examples

Example 1:

Input: root = [3,9,20,null,null,15,7]
Output: [[3],[9,20],[15,7]]
Explanation: 
Level 0: [3]
Level 1: [9, 20]
Level 2: [15, 7]

Example 2:

Input: root = [1]
Output: [[1]]
Explanation: Single node tree.

Example 3:

Input: root = []
Output: []
Explanation: Empty tree.

Constraints

  • The number of nodes in the tree is in the range [0, 2000].
  • -1000 <= Node.val <= 1000

Approach 1: BFS with Queue (Optimal)

Algorithm

Use breadth-first search with a queue to process nodes level by level.

Steps:

  1. Initialize a queue with the root node
  2. While queue is not empty:
    • Get the current level size
    • Process all nodes at current level
    • Add their children to queue for next level
  3. Return the result

Implementation

Python:

from collections import deque

def levelOrder(root):
    """
    Level order traversal using BFS with queue
    Time: O(n)
    Space: O(w) where w is maximum width
    """
    if not root:
        return []
    
    result = []
    queue = deque([root])
    
    while queue:
        level_size = len(queue)
        current_level = []
        
        for _ in range(level_size):
            node = queue.popleft()
            current_level.append(node.val)
            
            if node.left:
                queue.append(node.left)
            if node.right:
                queue.append(node.right)
        
        result.append(current_level)
    
    return result

Java:

import java.util.*;

class Solution {
    /**
     * Level order traversal using BFS with queue
     * Time: O(n)
     * Space: O(w) where w is maximum width
     */
    public List<List<Integer>> levelOrder(TreeNode root) {
        List<List<Integer>> result = new ArrayList<>();
        if (root == null) {
            return result;
        }
        
        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(root);
        
        while (!queue.isEmpty()) {
            int levelSize = queue.size();
            List<Integer> currentLevel = new ArrayList<>();
            
            for (int i = 0; i < levelSize; i++) {
                TreeNode node = queue.poll();
                currentLevel.add(node.val);
                
                if (node.left != null) {
                    queue.offer(node.left);
                }
                if (node.right != null) {
                    queue.offer(node.right);
                }
            }
            
            result.add(currentLevel);
        }
        
        return result;
    }
}

Go:

func levelOrder(root *TreeNode) [][]int {
    /**
     * Level order traversal using BFS with queue
     * Time: O(n)
     * Space: O(w) where w is maximum width
     */
    if root == nil {
        return [][]int{}
    }
    
    var result [][]int
    queue := []*TreeNode{root}
    
    for len(queue) > 0 {
        levelSize := len(queue)
        currentLevel := make([]int, 0, levelSize)
        
        for i := 0; i < levelSize; i++ {
            node := queue[0]
            queue = queue[1:] // Remove first element
            currentLevel = append(currentLevel, node.Val)
            
            if node.Left != nil {
                queue = append(queue, node.Left)
            }
            if node.Right != nil {
                queue = append(queue, node.Right)
            }
        }
        
        result = append(result, currentLevel)
    }
    
    return result
}

JavaScript:

/**
 * Level order traversal using BFS with queue
 * Time: O(n)
 * Space: O(w) where w is maximum width
 */
function levelOrder(root) {
    if (!root) {
        return [];
    }
    
    const result = [];
    const queue = [root];
    
    while (queue.length > 0) {
        const levelSize = queue.length;
        const currentLevel = [];
        
        for (let i = 0; i < levelSize; i++) {
            const node = queue.shift();
            currentLevel.push(node.val);
            
            if (node.left) {
                queue.push(node.left);
            }
            if (node.right) {
                queue.push(node.right);
            }
        }
        
        result.push(currentLevel);
    }
    
    return result;
}

C#:

using System.Collections.Generic;

public class Solution {
    /// <summary>
    /// Level order traversal using BFS with queue
    /// Time: O(n)
    /// Space: O(w) where w is maximum width
    /// </summary>
    public IList<IList<int>> LevelOrder(TreeNode root) {
        var result = new List<IList<int>>();
        if (root == null) {
            return result;
        }
        
        var queue = new Queue<TreeNode>();
        queue.Enqueue(root);
        
        while (queue.Count > 0) {
            int levelSize = queue.Count;
            var currentLevel = new List<int>();
            
            for (int i = 0; i < levelSize; i++) {
                var node = queue.Dequeue();
                currentLevel.Add(node.val);
                
                if (node.left != null) {
                    queue.Enqueue(node.left);
                }
                if (node.right != null) {
                    queue.Enqueue(node.right);
                }
            }
            
            result.Add(currentLevel);
        }
        
        return result;
    }
}

Complexity Analysis

  • Time Complexity: O(n) - Visit each node exactly once
  • Space Complexity: O(w) - Queue stores at most one level of nodes

Key Insights

  1. BFS Approach: Use queue to process nodes level by level
  2. Level Size Tracking: Process exact number of nodes per level
  3. DFS Alternative: Use recursion with level parameter
  4. Space Trade-off: BFS uses O(w) space, DFS uses O(h) space
  5. Level Building: Build result level by level

Edge Cases

  1. Empty Tree: Return empty list
  2. Single Node: Return single level with one node
  3. Skewed Tree: Handle left-skewed or right-skewed trees
  4. Perfect Tree: Handle balanced trees efficiently
  5. Large Trees: Handle trees with many levels

Test Cases

def test_levelOrder():
    # Test case 1: Normal tree
    root1 = TreeNode(3)
    root1.left = TreeNode(9)
    root1.right = TreeNode(20)
    root1.right.left = TreeNode(15)
    root1.right.right = TreeNode(7)
    
    result1 = levelOrder(root1)
    expected1 = [[3], [9, 20], [15, 7]]
    assert result1 == expected1
    
    # Test case 2: Single node
    root2 = TreeNode(1)
    result2 = levelOrder(root2)
    expected2 = [[1]]
    assert result2 == expected2
    
    # Test case 3: Empty tree
    result3 = levelOrder(None)
    expected3 = []
    assert result3 == expected3
    
    print("All tests passed!")

test_levelOrder()

Follow-up Questions

  1. Zigzag Level Order: Traverse levels in alternating directions?
  2. Bottom-up Level Order: Start from bottom level?
  3. Level Order Successor: Find next node in level order?
  4. Level Order Predecessor: Find previous node in level order?
  5. Level Order with Nulls: Include null nodes in traversal?

Common Mistakes

  1. Not Tracking Level Size: Forgetting to process exact number of nodes per level
  2. Wrong Queue Operations: Using wrong queue methods (add vs offer)
  3. Null Checks: Not checking for null nodes before processing
  4. Level Indexing: Off-by-one errors in level tracking
  5. Space Complexity: Confusing BFS and DFS space complexities

Interview Tips

  1. Start with BFS: Most intuitive approach for level order
  2. Explain Level Tracking: Show understanding of level size concept
  3. Discuss Trade-offs: Compare BFS vs DFS approaches
  4. Handle Edge Cases: Mention empty tree and single node cases
  5. Follow-up Ready: Be prepared for zigzag and other variations

Concept Explanations

BFS Approach: Breadth-first search processes nodes level by level, which naturally gives us the level order traversal. We use a queue to maintain the order of nodes to be processed.

Level Size Tracking: The key insight is to process exactly the number of nodes that are currently in the queue (which represents one level) before moving to the next level.

DFS Alternative: We can also use depth-first search by keeping track of the current level and adding nodes to the appropriate level in our result structure.

Space Complexity: BFS uses O(w) space where w is the maximum width of the tree (maximum number of nodes at any level), while DFS uses O(h) space where h is the height of the tree (recursion stack depth).

Why BFS is Preferred: BFS naturally processes nodes level by level, making it more intuitive for level order traversal. It also has better space complexity for wide trees.

Queue Operations: We use a queue to maintain the order of nodes. We enqueue children of current level nodes and dequeue nodes to process them in the correct order.