Level-Order Traversal
Overview
Level-order traversal (also called breadth-first traversal) processes binary tree nodes level by level, visiting all nodes at depth d before nodes at depth d+1. It uses a queue to maintain the order of nodes to visit, making it perfect for problems requiring level-by-level processing.
Unlike preorder, inorder, or postorder traversals (which are depth-first), level-order traversal explores the tree breadth-first, ensuring you see nodes level by level from top to bottom. This makes it ideal for problems like finding level averages, right-side view, or processing nodes at each level.
Key characteristics:
- Level-by-level - Process all nodes at current level before next
- Queue-based - Uses queue for FIFO order
- Top to bottom - Starts at root, goes level by level
- Left to right - Within each level, processes left to right
- Complete - Visits all nodes in the tree
How Level-Order Traversal Works
Level-order traversal follows these steps:
- Start with root node, add to queue
- While queue is not empty:
- Get current level size (number of nodes at this level)
- Process all nodes at current level
- Add all children of current level nodes to queue
- Move to next level
- Repeat until queue is empty
The queue ensures nodes are processed in level order, maintaining the breadth-first exploration.
Basic Level-Order Traversal
Simple level-order traversal returning values:
class TreeNode {
val: number;
left: TreeNode | null = null;
right: TreeNode | null = null;
constructor(val: number) {
this.val = val;
}
}
function levelOrder(root: TreeNode | null): number[] {
if (!root) return [];
const queue: TreeNode[] = [root];
const result: number[] = [];
while (queue.length > 0) {
const node = queue.shift()!;
result.push(node.val);
if (node.left) queue.push(node.left);
if (node.right) queue.push(node.right);
}
return result;
}Time: O(n), Space: O(n)
Output: [3, 9, 20, 15, 7] for tree:
3
/ \
9 20
/ \
15 7Pattern 1: Level-Order with Level Separation
Return nodes grouped by level:
function levelOrderLevels(root: TreeNode | null): number[][] {
if (!root) return [];
const queue: TreeNode[] = [root];
const result: number[][] = [];
while (queue.length > 0) {
const levelSize = queue.length;
const level: number[] = [];
// Process all nodes at current level
for (let i = 0; i < levelSize; i++) {
const node = queue.shift()!;
level.push(node.val);
// Add children for next level
if (node.left) queue.push(node.left);
if (node.right) queue.push(node.right);
}
result.push(level);
}
return result;
}Time: O(n), Space: O(n)
Output: [[3], [9, 20], [15, 7]]
Key insight: Track levelSize before processing to separate levels.
Pattern 2: Right Side View
Return rightmost node at each level:
function rightSideView(root: TreeNode | null): number[] {
if (!root) return [];
const queue: TreeNode[] = [root];
const result: number[] = [];
while (queue.length > 0) {
const levelSize = queue.length;
for (let i = 0; i < levelSize; i++) {
const node = queue.shift()!;
// Rightmost node at this level
if (i === levelSize - 1) {
result.push(node.val);
}
if (node.left) queue.push(node.left);
if (node.right) queue.push(node.right);
}
}
return result;
}Time: O(n), Space: O(n)
Output: [3, 20, 7]
Pattern 3: Left Side View
Return leftmost node at each level:
function leftSideView(root: TreeNode | null): number[] {
if (!root) return [];
const queue: TreeNode[] = [root];
const result: number[] = [];
while (queue.length > 0) {
const levelSize = queue.length;
for (let i = 0; i < levelSize; i++) {
const node = queue.shift()!;
// Leftmost node at this level
if (i === 0) {
result.push(node.val);
}
if (node.left) queue.push(node.left);
if (node.right) queue.push(node.right);
}
}
return result;
}Time: O(n), Space: O(n)
Pattern 4: Average of Levels
Calculate average value at each level:
function averageOfLevels(root: TreeNode | null): number[] {
if (!root) return [];
const queue: TreeNode[] = [root];
const result: number[] = [];
while (queue.length > 0) {
const levelSize = queue.length;
let sum = 0;
for (let i = 0; i < levelSize; i++) {
const node = queue.shift()!;
sum += node.val;
if (node.left) queue.push(node.left);
if (node.right) queue.push(node.right);
}
result.push(sum / levelSize);
}
return result;
}Time: O(n), Space: O(n)
Output: [3.0, 14.5, 11.0]
Pattern 5: Zigzag Level Order
Traverse level by level, alternating left-to-right and right-to-left:
function zigzagLevelOrder(root: TreeNode | null): number[][] {
if (!root) return [];
const queue: TreeNode[] = [root];
const result: number[][] = [];
let leftToRight = true;
while (queue.length > 0) {
const levelSize = queue.length;
const level: number[] = [];
for (let i = 0; i < levelSize; i++) {
const node = queue.shift()!;
// Add to level based on direction
if (leftToRight) {
level.push(node.val);
} else {
level.unshift(node.val);
}
if (node.left) queue.push(node.left);
if (node.right) queue.push(node.right);
}
result.push(level);
leftToRight = !leftToRight;
}
return result;
}Time: O(n), Space: O(n)
Output: [[3], [20, 9], [15, 7]]
Pattern 6: Maximum Value at Each Level
Find maximum value at each level:
function largestValues(root: TreeNode | null): number[] {
if (!root) return [];
const queue: TreeNode[] = [root];
const result: number[] = [];
while (queue.length > 0) {
const levelSize = queue.length;
let maxVal = -Infinity;
for (let i = 0; i < levelSize; i++) {
const node = queue.shift()!;
maxVal = Math.max(maxVal, node.val);
if (node.left) queue.push(node.left);
if (node.right) queue.push(node.right);
}
result.push(maxVal);
}
return result;
}Time: O(n), Space: O(n)
Pattern 7: Minimum Depth
Find minimum depth (distance to nearest leaf):
function minDepth(root: TreeNode | null): number {
if (!root) return 0;
const queue: [TreeNode, number][] = [[root, 1]];
while (queue.length > 0) {
const [node, depth] = queue.shift()!;
// Found leaf node
if (!node.left && !node.right) {
return depth;
}
if (node.left) queue.push([node.left, depth + 1]);
if (node.right) queue.push([node.right, depth + 1]);
}
return 0;
}Time: O(n), Space: O(n)
Key insight: BFS finds minimum depth because it explores level by level.
Pattern 8: N-ary Tree Level Order
Level-order traversal for N-ary trees:
class NaryNode {
val: number;
children: NaryNode[] = [];
constructor(val: number) {
this.val = val;
}
}
function naryLevelOrder(root: NaryNode | null): number[][] {
if (!root) return [];
const queue: NaryNode[] = [root];
const result: number[][] = [];
while (queue.length > 0) {
const levelSize = queue.length;
const level: number[] = [];
for (let i = 0; i < levelSize; i++) {
const node = queue.shift()!;
level.push(node.val);
// Add all children
for (const child of node.children) {
queue.push(child);
}
}
result.push(level);
}
return result;
}Time: O(n), Space: O(n)
Pattern 9: Level Order Successor
Find next node in level-order traversal after given node:
function levelOrderSuccessor(root: TreeNode | null, target: number): TreeNode | null {
if (!root) return null;
const queue: TreeNode[] = [root];
while (queue.length > 0) {
const node = queue.shift()!;
if (node.left) queue.push(node.left);
if (node.right) queue.push(node.right);
// If current node is target, next node is successor
if (node.val === target && queue.length > 0) {
return queue[0];
}
}
return null;
}Time: O(n), Space: O(n)
Pattern 10: Connect Nodes at Same Level
Connect each node to its right neighbor at the same level:
class NodeWithNext {
val: number;
left: NodeWithNext | null = null;
right: NodeWithNext | null = null;
next: NodeWithNext | null = null;
constructor(val: number) {
this.val = val;
}
}
function connect(root: NodeWithNext | null): NodeWithNext | null {
if (!root) return null;
const queue: NodeWithNext[] = [root];
while (queue.length > 0) {
const levelSize = queue.length;
for (let i = 0; i < levelSize; i++) {
const node = queue.shift()!;
// Connect to next node in same level
if (i < levelSize - 1) {
node.next = queue[0];
}
if (node.left) queue.push(node.left);
if (node.right) queue.push(node.right);
}
}
return root;
}Time: O(n), Space: O(n)
When to Use Level-Order Traversal
Use level-order traversal when you see these signals:
- "Level by level" - Process nodes level by level
- "Right side view" or "left side view" - Need nodes at level boundaries
- "Level average" or "level sum" - Aggregate values at each level
- "Minimum depth" - Find distance to nearest leaf
- "Connect nodes" - Link nodes at same level
- "Zigzag traversal" - Alternate direction per level
- "Level maximum/minimum" - Find extremum at each level
Key distinction:
- Level-order (BFS) - Process level by level, queue-based
- Preorder/Inorder/Postorder (DFS) - Depth-first, stack/recursion-based
Level-Order vs Other Traversals
| Traversal | Order | Data Structure | Use Case |
|-----------|-------|----------------|----------|
| Level-order | Level by level | Queue | Level-based problems |
| Preorder | Root → Left → Right | Stack | Copy tree, prefix expressions |
| Inorder | Left → Root → Right | Stack | BST sorted order |
| Postorder | Left → Right → Root | Stack | Delete tree, postfix expressions |
Remember:
- Level-order for level-based problems, right/left view, level averages
- DFS traversals for tree structure problems, BST operations
Template (Basic Level-Order)
function levelOrderTemplate(root: TreeNode | null): number[] {
if (!root) return [];
const queue: TreeNode[] = [root];
const result: number[] = [];
while (queue.length > 0) {
const node = queue.shift()!;
result.push(node.val);
if (node.left) queue.push(node.left);
if (node.right) queue.push(node.right);
}
return result;
}Template (Level-by-Level)
function levelOrderLevelsTemplate(root: TreeNode | null): number[][] {
if (!root) return [];
const queue: TreeNode[] = [root];
const result: number[][] = [];
while (queue.length > 0) {
const levelSize = queue.length;
const level: number[] = [];
for (let i = 0; i < levelSize; i++) {
const node = queue.shift()!;
level.push(node.val);
if (node.left) queue.push(node.left);
if (node.right) queue.push(node.right);
}
result.push(level);
}
return result;
}Template (Level with Processing)
function levelOrderProcessTemplate(root: TreeNode | null): void {
if (!root) return;
const queue: TreeNode[] = [root];
while (queue.length > 0) {
const levelSize = queue.length;
// Process all nodes at current level
for (let i = 0; i < levelSize; i++) {
const node = queue.shift()!;
// Process node (e.g., find max, calculate sum)
// processNode(node);
// Add children for next level
if (node.left) queue.push(node.left);
if (node.right) queue.push(node.right);
}
// Process level (e.g., add to result, calculate average)
// processLevel();
}
}Time and Space Complexity
Binary Tree Level-Order
- Time: O(n) where n is number of nodes
- Space: O(n) worst case (complete binary tree)
N-ary Tree Level-Order
- Time: O(n) where n is total nodes
- Space: O(n) worst case
Space analysis:
- Best case: O(1) for skewed tree (one node per level)
- Worst case: O(n) for complete binary tree (last level has ~n/2 nodes)
- Average case: O(n) for balanced trees
Practice Tips
- Track level size - Get
levelSize = queue.lengthbefore processing level - Process level by level - Use for loop with levelSize to process all nodes at current level
- Add children after processing - Add children to queue after processing current node
- Handle null root - Always check if root is null
- Use queue, not stack - Level-order requires FIFO order
- Track direction - For zigzag, toggle direction after each level
- Early termination - Can return early for problems like minimum depth
Common Mistakes
- Not tracking level size - Queue size changes during level processing
- Processing while adding - Should process all nodes at level before adding next level
- Using stack - Wrong data structure breaks level-order
- Not handling null - Forgetting to check
node.leftandnode.rightbefore adding - Wrong loop condition - Using
queue.lengthdirectly in for loop causes issues - Not resetting level - Forgetting to create new level array for each level
- Index errors - Off-by-one when finding rightmost/leftmost node
Real-World Applications
Level-order traversal is used in:
- Tree Visualization - Display trees level by level
- Printing Trees - Pretty print trees with indentation
- Tree Serialization - Serialize tree level by level
- Level-Based Analysis - Analyze tree structure by level
- Tree Balancing - Check if tree is balanced level by level
- Hierarchical Data - Process organizational hierarchies
- File System Traversal - Traverse directory structure level by level
- Network Topology - Analyze network structure by hops
Related Concepts
- Breadth-First Search (BFS) - Same algorithm, different context (graphs)
- Queue - Data structure used for level-order traversal
- Tree Traversals - Preorder, inorder, postorder (DFS)
- Binary Tree - Most common tree structure for level-order
- N-ary Tree - Generalization to multiple children
- Minimum Depth - Level-order finds minimum depth efficiently
Summary
Level-order traversal is a fundamental tree traversal technique:
- Queue-based - Uses queue for FIFO order
- Level-by-level - Processes all nodes at current level before next
- Top to bottom - Starts at root, goes level by level
- Complete - Visits all nodes in the tree
- Essential - Foundation for many tree problems
Key takeaways:
- Track level size before processing each level
- Process all nodes at current level before adding next level
- Use queue (not stack) for level-order traversal
- Essential for level-based problems (right view, averages, etc.)
- Foundation for BFS on trees
Mastering level-order traversal will help you solve level-based tree problems efficiently in coding interviews.