Level-Order Traversal

Trees
avg O(n)

Level-order traversal reads a binary tree the way you read a page: the root first, then its children left to right, then their children, row by row down to the last leaf. Under the costume it is plain breadth-first search - the same queue, pointed at a tree. It powers the famous [[level0], [level1], ...] answer shape from LeetCode, plus zigzag prints and "right side view" problems. One small trick - freezing the queue length before each round - turns the flat stream of nodes into clean per-level groups.

Watch the rows peel off

Press play - or drag the timeline and step through it yourself.
Level 1Nodes output 0Levels done 0Step 1 / 26
3791012151820

Read this tree the way you read a page: row by row, top to bottom, left to right. A queue makes that order automatic.

just dequeued in the queue output link followed

Complexity

Best
O(n)
Input already in order
Average
O(n)
Normal mixed input
Worst
O(n)
Worst possible input
Space
O(n)
Extra memory used

Greeting a family reunion, generation by generation

You arrive at a family reunion and want to greet everyone in a fair order: grandma first, then her children, then the grandchildren. So you keep a to-greet list. It starts with only grandma's name. Each time you greet the person at the top of the list, you add that person's children to the bottom. Because children always join the back of the line, a grandchild can never be greeted before an aunt or uncle - the list serves whole generations in order. That to-greet list is the queue, and the reunion is level-order traversal.

How it works, step by step

  1. Put the root into a queue. The queue always holds the nodes waiting to be read.

  2. Before each round, read `queue.length` once and save it as `size`. Exactly that many nodes belong to the current level.

  3. Dequeue `size` nodes one by one. Each dequeued node joins the current level's array.

  4. As you dequeue a node, push its left child, then its right child, to the back of the queue. They belong to the next level, safely behind the frozen count.

  5. After `size` nodes, the level is complete. Push its array into the result and start the next round.

  6. When the queue is empty, every node was read exactly once - O(n) time. The queue held at most one level, up to about n/2 nodes on a bushy tree.

The code, in JavaScript

The simple version: one queue, one output list. Note the index pointer - `shift()` would work, but it is O(n) per call. `head++` gives a real O(n) total.

javascript
const node = (val, left = null, right = null) => ({ val, left, right });

// The tree from the animation. Level sizes: 1, 2, 3, 2.
const root = node(3,
  node(9, null, node(12)),
  node(20, node(15, node(10), node(18)), node(7))
);

function levelOrderFlat(root) {
  if (!root) return [];
  const queue = [root];
  const order = [];
  let head = 0;                       // index pointer instead of shift()

  while (head < queue.length) {
    const cur = queue[head++];        // "dequeue" without moving memory
    order.push(cur.val);
    if (cur.left) queue.push(cur.left);
    if (cur.right) queue.push(cur.right);
  }

  return order;
}

levelOrderFlat(root);
// → [3, 9, 20, 12, 15, 7, 10, 18]

Dry run: reading the tree level by level

The same steps the visualizer plays, written as a table. Generated by running the real algorithm - not written by hand.

StepLevelWhat happened
11Take 3 out of the queue and output it. Output: [3]. Queue: [].
21Level 0 done: [3]. Queue now holds level 1: [9, 20].
32Take 9 out of the queue and output it. Output: [3, 9]. Queue: [20].
42Take 20 out of the queue and output it. Output: [3, 9, 20]. Queue: [12].
52Level 1 done: [9, 20]. Queue now holds level 2: [12, 15, 7].
63Take 12 out and output it. It is a leaf - nothing to add. Output: [3, 9, 20, 12]. Queue: [15, 7].
73Take 15 out of the queue and output it. Output: [3, 9, 20, 12, 15]. Queue: [7].
83Take 7 out and output it. It is a leaf - nothing to add. Output: [3, 9, 20, 12, 15, 7]. Queue: [10, 18].
93Level 2 done: [12, 15, 7]. Queue now holds level 3: [10, 18].
104Take 10 out and output it. It is a leaf - nothing to add. Output: [3, 9, 20, 12, 15, 7, 10]. Queue: [18].
114Take 18 out and output it. It is a leaf - nothing to add. Output: [3, 9, 20, 12, 15, 7, 10, 18]. Queue: [].
124Level 3 done: [10, 18]. The queue is empty - every node has been read.

Good choice when…

  • The answer is per level: row averages, zigzag order, right side view, largest value in each row. All of them are this loop with one line changed.
  • You need the minimum depth or the nearest matching node: the first level that satisfies the condition wins, and you can stop early.
  • You are printing or serializing a tree top to bottom. Binary heaps are literally stored this way - an array in level order.
  • The tree is very deep: the queue version cannot blow the call stack, unlike recursive DFS traversals.

Bad choice when…

  • You want a BST's values in sorted order. That is in-order traversal's free gift - level order returns nothing sorted.
  • Memory is tight and the tree is wide: the queue holds a whole level, up to about n/2 nodes at once. DFS holds only the height.
  • The problem is naturally recursive - subtree sums, tree height, path sums. Those read much cleaner as DFS.

Common mistakes

  • Read `const size = queue.length` before the inner loop. Checking `queue.length` live inside the loop counts the children you just pushed, and levels silently merge into one giant row. This is the number one bug in this problem.
  • `array.shift()` is O(n) - it moves every remaining element one slot left. Called n times, it turns the traversal into O(n²). Use an index pointer (`queue[head++]`) on big trees.
  • No BST needed. Level order never compares values, so it works on any binary tree. Do not confuse it with in-order, whose sorted-output trick only works on a BST.
  • Level order alone cannot serialize a tree: [3, 9, 20] does not say which child is left and which is right, or where a child is missing. To rebuild the shape you must also record the `null` children - that is exactly LeetCode's `[3,9,20,null,null,15,7]` format.

Level-Order Traversal vs. its closest relatives

AlgorithmBestAverageWorstSpaceStable
Level-Order Traversalthis pageO(n)O(n)O(n)O(n)-
Breadth-First SearchO(V + E)O(V + E)O(V + E)O(V)-
Tree TraversalsO(n)O(n)O(n)O(h)-
Binary Search TreeO(log n)O(log n)O(n)O(n)-