Problem Walkthrough

Zigzag Level Order Traversal LeetCode 103

LeetCode 103 Zigzag Level Order Traversal extends standard BFS with a direction flag that alternates each level: left-to-right on even levels, right-to-left on odd levels. The simplest implementation collects each level normally and reverses odd levels before appending to the result. An alternative deque-based approach appends or prepends values based on the direction flag, avoiding the explicit reverse step.

8 min read|

Zigzag Level Order

LeetCode 103

Problem Overview

LeetCode 103, Binary Tree Zigzag Level Order Traversal, asks you to return the level order traversal of a binary tree's values but in a zigzag pattern: the first level goes left to right, the second level goes right to left, the third goes left to right again, and so on. The result is a list of lists, each inner list containing the values for one level.

For a tree with root 3, left child 9, right child 20, and 20's children 15 and 7, the expected output is [[3], [20, 9], [15, 7]]. The root level is left-to-right (just [3]), the second level is right-to-left ([20, 9]), and the third level is left-to-right again ([15, 7]).

This is a direct extension of LeetCode 102 Binary Tree Level Order Traversal. If you can solve 102, solving 103 requires only one extra step: tracking a direction flag and reversing (or deque-prepending) alternate levels. It is a common interview problem at Amazon, Microsoft, and Bloomberg.

  • Input: root of a binary tree (may be null)
  • Output: list of lists of integers in zigzag level order
  • Constraints: 0 to 2000 nodes, node values -100 to 100
  • Example: [3,9,20,null,null,15,7] → [[3],[20,9],[15,7]]
  • Example: [1] → [[1]] (single node)
  • Example: [] → [] (empty tree)

BFS with Direction Flag

The standard approach is to run a normal level-order BFS with a queue. After collecting all values for a level, check the direction flag. If the current direction is right-to-left, reverse the collected values before appending them to the result. Then toggle the flag for the next level.

The key insight is that BFS naturally gives you one full level at a time when you snapshot the queue size at the start of each level iteration. Process exactly that many nodes, collect their values, add their children to the queue, and then either append the values directly or append the reversed values depending on the flag.

This approach is clean, easy to explain in an interview, and directly extends LeetCode 102 with a single boolean and a conditional reverse. The reversal is O(w) where w is the width of the level, and the total work across all levels is O(n) since every node is visited exactly once.

💡

Simplest Approach: BFS + Reverse Odd Levels

The simplest approach is standard BFS plus reversing odd-indexed levels. Reversing a level array is O(w) where w is the level width, and total work is still O(n) because the sum of all level widths equals n. This is the approach interviewers expect — clean, correct, and easy to verify.

BFS Implementation

Initialize a queue with the root node and a boolean leftToRight set to true. On each iteration of the outer while loop, read the current queue size — this is the number of nodes in the current level. Use an inner for loop to dequeue exactly that many nodes, collect their values, and enqueue their non-null children.

After the inner loop finishes, if leftToRight is false, reverse the collected level values. Append the (possibly reversed) values to the result list. Toggle leftToRight and continue to the next level. This processes the entire tree in O(n) time and O(n) space for the queue and result.

Edge cases: if root is null, return an empty list immediately. If a node has only a left or only a right child, only that child is enqueued — the BFS handles sparse trees naturally without any special casing.

  1. 1If root is null, return []
  2. 2Initialize queue = [root], leftToRight = true, result = []
  3. 3While queue is not empty:
  4. 4 levelSize = len(queue); level = []
  5. 5 For i in range(levelSize): node = dequeue; level.append(node.val); enqueue non-null children
  6. 6 If not leftToRight: level.reverse()
  7. 7 result.append(level); toggle leftToRight
  8. 8Return result

Deque-Based Alternative

Instead of collecting level values into a list and reversing, you can collect them into a deque (double-ended queue) and choose the insertion end based on the direction flag. If left-to-right, append to the right of the deque. If right-to-left, appendleft to the left of the deque. The deque is then converted to a list before being added to the result.

This approach avoids the explicit reverse call on each odd level. Since deque appendleft is O(1), the per-level work is the same O(w). The total time complexity remains O(n). In Python, collections.deque supports appendleft natively. In Java, ArrayDeque supports addFirst and addLast.

The deque approach can feel more elegant because it processes nodes in the natural BFS order but builds the level output in the correct zigzag order on the fly. However, it introduces the cognitive overhead of tracking which end to insert into, which can lead to bugs if the direction logic is inverted.

ℹ️

Deque Avoids Reversing but Adds Complexity

The deque approach avoids the explicit reverse step by inserting at the correct end of the output list during traversal. However, for interviews the reverse approach is cleaner to explain and just as fast — O(n) total. Use the deque approach only if you are comfortable with deque semantics and the interviewer has specifically asked to avoid extra work.

DFS Alternative

A DFS approach using recursion can also solve this problem. Pass a level parameter alongside the node. Maintain the result as a list of lists. When you visit a node at a given level, if that level's list does not yet exist in the result, append a new empty list. Then insert the node's value: if the level is even (left-to-right), append to the end of that level's list; if the level is odd (right-to-left), insert at the front (index 0).

Inserting at index 0 is O(w) for a Python list, so in the worst case (a skewed tree) the DFS approach can be O(n^2). For balanced trees it is fine. In Java, using ArrayDeque for each level and calling addFirst or addLast achieves O(1) insertion either way, restoring O(n) total complexity.

The BFS approach is generally preferred for level-order problems because it naturally processes levels one at a time, making the level grouping explicit. The DFS approach requires careful bookkeeping of the result list size and insertion position, which is more error-prone under interview time pressure.

  1. 1def dfs(node, level):
  2. 2 if node is None: return
  3. 3 if level >= len(result): result.append([])
  4. 4 if level % 2 == 0: result[level].append(node.val) # left-to-right
  5. 5 else: result[level].insert(0, node.val) # right-to-left: insert at front
  6. 6 dfs(node.left, level + 1)
  7. 7 dfs(node.right, level + 1)

Code Walkthrough — Python and Java

Python BFS + reverse: from collections import deque; def zigzagLevelOrder(root): if not root: return []; queue = deque([root]); result = []; leftToRight = True; while queue: size = len(queue); level = []; for _ in range(size): node = queue.popleft(); level.append(node.val); if node.left: queue.append(node.left); if node.right: queue.append(node.right); if not leftToRight: level.reverse(); result.append(level); leftToRight = not leftToRight; return result. Time O(n), space O(n).

Java BFS + reverse: public List<List<Integer>> zigzagLevelOrder(TreeNode root) { List<List<Integer>> result = new ArrayList<>(); if (root == null) return result; Queue<TreeNode> queue = new LinkedList<>(); queue.offer(root); boolean leftToRight = true; while (!queue.isEmpty()) { int size = queue.size(); List<Integer> level = new ArrayList<>(); for (int i = 0; i < size; i++) { TreeNode node = queue.poll(); level.add(node.val); if (node.left != null) queue.offer(node.left); if (node.right != null) queue.offer(node.right); } if (!leftToRight) Collections.reverse(level); result.add(level); leftToRight = !leftToRight; } return result; }.

Both solutions are direct implementations of the BFS + direction flag approach. The Python version uses collections.deque for O(1) popleft. The Java version uses LinkedList as the queue. Both toggle leftToRight after each level and call reverse on odd-indexed levels. The overall time complexity is O(n) and space complexity is O(n) for the queue and result.

⚠️

Toggle Direction After Each Level, Not Inside the Inner Loop

Don't forget to toggle the direction flag after each level, not inside the inner node-processing loop. A common bug is placing leftToRight = !leftToRight inside the for loop that processes individual nodes, which toggles the flag for every node instead of every level. The toggle must be the last statement in the outer while loop, after appending the level to the result.

Ready to master algorithm patterns?

YeetCode flashcards help you build pattern recognition through active recall and spaced repetition.

Start practicing now