Want Help Cracking FAANG?

(Then click this)

×
Back to Question Bank

103. Binary Tree Zigzag Level Order Traversal - Leetcode Solution

Problem Description

The Binary Tree Zigzag Level Order Traversal problem asks you to traverse a binary tree in a level-by-level manner, but with a twist: the order alternates between left-to-right and right-to-left at each level.

  • Given the root node of a binary tree, return its zigzag level order traversal as a list of lists of node values.
  • The traversal should start at the root (level 0), process all nodes at that level from left to right, then at the next level process from right to left, and so on, alternating at each level.
  • Each inner list in your result should represent one level of the tree, in the order it was traversed.
  • You may assume the tree nodes are defined as:
    class TreeNode:
        def __init__(self, val=0, left=None, right=None):
            self.val = val
            self.left = left
            self.right = right
          

Constraints:

  • There is only one valid traversal order per tree.
  • Do not reuse nodes or skip any nodes.

Thought Process

The problem is a variant of the classic level order traversal (also known as breadth-first traversal) of a binary tree. In the standard approach, you process each level from left to right. However, the zigzag requirement means that the direction alternates at each level: left-to-right, then right-to-left, then left-to-right, and so on.

My first thought was to use a queue to perform the level order traversal, as is typical. However, to alternate the direction, I realized I could reverse the list of values for every other level, or collect values in different orders depending on the level.

A brute-force way would be to collect all node values at each level, then reverse the list for every odd-numbered level. But I wondered if there’s a more elegant way, perhaps by using a double-ended queue (deque) or by carefully controlling the order in which children are added to the queue.

Ultimately, the optimized approach is to use a standard queue for BFS, and at each level, either append values normally or in reverse, depending on the current level's parity (even or odd).

Solution Approach

Here’s a step-by-step breakdown of the zigzag traversal algorithm:

  1. Initialize a queue: Start with the root node in a queue. This queue will help us process nodes level by level.
  2. Track direction: Use a boolean flag (e.g., left_to_right) to keep track of the current direction. Start with True for left-to-right.
  3. Process levels: While the queue is not empty:
    • Determine the number of nodes at the current level (level_size).
    • Initialize an empty list (level_nodes) to hold the values of the current level.
    • For each node at this level:
      • Dequeue the node from the queue.
      • Append its value to level_nodes.
      • Enqueue its left and right children (if they exist).
    • If left_to_right is False, reverse level_nodes before adding it to the result.
    • Append level_nodes to the result list.
    • Flip the left_to_right flag for the next level.
  4. Return the result: After processing all levels, return the result list.

Why this works: The queue ensures we process nodes level by level, and the direction flag ensures we alternate the order of values as required. This approach is efficient and easy to implement.

Example Walkthrough

Consider the following binary tree:

        1
       / \
      2   3
     / \   \
    4   5   6
  

Let’s walk through the traversal step by step:

  1. Level 0 (left-to-right): Only node is 1. Output: [1]
  2. Level 1 (right-to-left): Nodes are 2 and 3. Output: [3, 2]
  3. Level 2 (left-to-right): Nodes are 4, 5, and 6. Output: [4, 5, 6]
  4. Result:
    [
      [1],
      [3, 2],
      [4, 5, 6]
    ]
          

Notice how the order alternates at each level, producing the desired zigzag pattern.

Time and Space Complexity

  • Brute-force: Collect all values at each level, then reverse as needed. Each node is visited once, and reversing takes O(N) extra time in total. So, O(N) time, where N is the number of nodes.
  • Optimized approach: The queue processes each node exactly once, and reversing each level (if needed) costs at most O(width of that level), so total time is still O(N).
  • Space complexity: The queue and the output list both store at most O(N) elements (all nodes at the largest level and the output). So, O(N) space.

Summary

The Binary Tree Zigzag Level Order Traversal problem is a twist on classic BFS traversal. By using a queue and a direction flag, we can efficiently traverse each level in alternating orders. The approach is elegant because it leverages the natural level-order structure of BFS, requiring only minor modifications to achieve the zigzag pattern. This results in a clear, maintainable, and optimal solution.

Code Implementation

from collections import deque

class Solution:
    def zigzagLevelOrder(self, root):
        if not root:
            return []
        result = []
        queue = deque([root])
        left_to_right = True
        
        while queue:
            level_size = len(queue)
            level_nodes = []
            for _ in range(level_size):
                node = queue.popleft()
                level_nodes.append(node.val)
                if node.left:
                    queue.append(node.left)
                if node.right:
                    queue.append(node.right)
            if not left_to_right:
                level_nodes.reverse()
            result.append(level_nodes)
            left_to_right = not left_to_right
        return result
      
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
#include <vector>
#include <queue>
using namespace std;

class Solution {
public:
    vector<vector<int>> zigzagLevelOrder(TreeNode* root) {
        vector<vector<int>> result;
        if (!root) return result;
        queue<TreeNode*> q;
        q.push(root);
        bool left_to_right = true;
        while (!q.empty()) {
            int level_size = q.size();
            vector<int> level_nodes;
            for (int i = 0; i < level_size; ++i) {
                TreeNode* node = q.front();
                q.pop();
                level_nodes.push_back(node->val);
                if (node->left) q.push(node->left);
                if (node->right) q.push(node->right);
            }
            if (!left_to_right)
                reverse(level_nodes.begin(), level_nodes.end());
            result.push_back(level_nodes);
            left_to_right = !left_to_right;
        }
        return result;
    }
};
      
import java.util.*;

class Solution {
    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 levelSize = queue.size();
            List<Integer> levelNodes = new ArrayList<>();
            for (int i = 0; i < levelSize; i++) {
                TreeNode node = queue.poll();
                levelNodes.add(node.val);
                if (node.left != null) queue.offer(node.left);
                if (node.right != null) queue.offer(node.right);
            }
            if (!leftToRight) {
                Collections.reverse(levelNodes);
            }
            result.add(levelNodes);
            leftToRight = !leftToRight;
        }
        return result;
    }
}
      
/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root
 * @return {number[][]}
 */
var zigzagLevelOrder = function(root) {
    if (!root) return [];
    const result = [];
    const queue = [root];
    let leftToRight = true;
    
    while (queue.length > 0) {
        const levelSize = queue.length;
        const levelNodes = [];
        for (let i = 0; i < levelSize; i++) {
            const node = queue.shift();
            levelNodes.push(node.val);
            if (node.left) queue.push(node.left);
            if (node.right) queue.push(node.right);
        }
        if (!leftToRight) {
            levelNodes.reverse();
        }
        result.push(levelNodes);
        leftToRight = !leftToRight;
    }
    return result;
};