Skip to content
intermediate Phase 4 · Trees & Heaps

Tree Traversals

Master inorder, preorder, postorder, and level-order traversals.

1h
5 problems
Topic Progress 0%

Iterative DFS Traversals

Why Iterative?

Recursive traversals use O(h) stack space. Iterative approaches give explicit control and avoid stack overflow for deep trees.

Node Definition

class TreeNode {
    int val;
    TreeNode left;
    TreeNode right;
    TreeNode(int val) { this.val = val; }
}

Iterative Pre-order (Root → Left → Right)

Use a stack, push right first (so left is processed first):

public List<Integer> preorder(TreeNode root) {
    List<Integer> result = new ArrayList<>();
    if (root == null) return result;
    Deque<TreeNode> stack = new ArrayDeque<>();
    stack.push(root);

    while (!stack.isEmpty()) {
        TreeNode node = stack.pop();
        result.add(node.val);
        if (node.right != null) stack.push(node.right);
        if (node.left != null) stack.push(node.left);
    }
    return result;
}

Iterative In-order (Left → Root → Right)

Traverse to leftmost, process, then move right:

public List<Integer> inorder(TreeNode root) {
    List<Integer> result = new ArrayList<>();
    De<TreeNode> stack = new ArrayDeque<>();
    TreeNode curr = root;

    while (curr != null || !stack.isEmpty()) {
        while (curr != null) {
            stack.push(curr);
            curr = curr.left;
        }
        curr = stack.pop();
        result.add(curr.val);
        curr = curr.right;
    }
    return result;
}

Iterative Post-order (Left → Right → Root)

Two-stack approach or single stack with tracking:

// Two-stack method
public List<Integer> postorder(TreeNode root) {
    List<Integer> result = new ArrayList<>();
    if (root == null) return result;
    Deque<TreeNode> stack1 = new ArrayDeque<>();
    Deque<TreeNode> stack2 = new ArrayDeque<>();
    stack1.push(root);

    while (!stack1.isEmpty()) {
        TreeNode node = stack1.pop();
        stack2.push(node);
        if (node.left != null) stack1.push(node.left);
        if (node.right != null) stack1.push(node.right);
    }
    while (!stack2.isEmpty()) {
        result.add(stack2.pop().val);
    }
    return result;
}

Morris Traversal - O(1) Space

Uses threaded binary tree concept. No stack or recursion:

public List<Integer> morrisInorder(TreeNode root) {
    List<Integer> result = new ArrayList<>();
    TreeNode curr = root;

    while (curr != null) {
        if (curr.left == null) {
            result.add(curr.val);
            curr = curr.right;
        } else {
            TreeNode predecessor = curr.left;
            while (predecessor.right != null && predecessor.right != curr) {
                predecessor = predecessor.right;
            }
            if (predecessor.right == null) {
                predecessor.right = curr;  // Create thread
                curr = curr.left;
            } else {
                predecessor.right = null;  // Remove thread
                result.add(curr.val);
                curr = curr.right;
            }
        }
    }
    return result;
}

Complexity Comparison

Method Time Space
Recursive O(n) O(h)
Iterative O(n) O(h)
Morris O(n) O(1)

BFS Level-Order Traversals

Basic Level-Order

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 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);
        }
        result.add(level);
    }
    return result;
}

Zigzag Level-Order

Alternate left-to-right and right-to-left:

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();
        LinkedList<Integer> level = new LinkedList<>();
        for (int i = 0; i < size; i++) {
            TreeNode node = queue.poll();
            if (leftToRight) {
                level.addLast(node.val);
            } else {
                level.addFirst(node.val);
            }
            if (node.left != null) queue.offer(node.left);
            if (node.right != null) queue.offer(node.right);
        }
        result.add(level);
        leftToRight = !leftToRight;
    }
    return result;
}

Right Side View

public List<Integer> rightSideView(TreeNode root) {
    List<Integer> result = new ArrayList<>();
    if (root == null) return result;
    Queue<TreeNode> queue = new LinkedList<>();
    queue.offer(root);

    while (!queue.isEmpty()) {
        int size = queue.size();
        for (int i = 0; i < size; i++) {
            TreeNode node = queue.poll();
            if (i == size - 1) result.add(node.val);  // Last in level
            if (node.left != null) queue.offer(node.left);
            if (node.right != null) queue.offer(node.right);
        }
    }
    return result;
}

Maximum Width

public int widthOfBinaryTree(TreeNode root) {
    if (root == null) return 0;
    int maxWidth = 0;
    Queue<pair<TreeNode, Integer>> queue = new LinkedList<>();
    queue.offer(new pair<>(root, 0));

    while (!queue.isEmpty()) {
        int size = queue.size();
        int minIndex = queue.peek().getValue();
        int first = 0, last = 0;
        for (int i = 0; i < size; i++) {
            var pair = queue.poll();
            TreeNode node = pair.getKey();
            int index = pair.getValue() - minIndex;  // Normalize
            if (i == 0) first = index;
            if (i == size - 1) last = index;
            if (node.left != null) queue.offer(new pair<>(node.left, 2 * index + 1));
            if (node.right != null) queue.offer(new pair<>(node.right, 2 * index + 2));
        }
        maxWidth = Math.max(maxWidth, last - first + 1);
    }
    return maxWidth;
}

BFS Template

Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
while (!queue.isEmpty()) {
    int size = queue.size();  // Fix size for current level
    for (int i = 0; i < size; i++) {
        TreeNode node = queue.poll();
        // Process node
        if (node.left != null) queue.offer(node.left);
        if (node.right != null) queue.offer(node.right);
    }
}

Practice Problems

0 / 3 solved
Binary Tree Right Side View
BFS Level-Order

Given the root of a binary tree, imagine yourself standing on the right side of it. Return the values of the nodes you can see.

Example:

Input: root = [1,2,3,null,5,null,4]

Output: [1,3,4]

Rightmost node at each level.

Optimal Solution — O(n) time, O(n) space

BFS, last node per level

class Solution {
    public List<Integer> rightSideView(TreeNode root) {
        List<Integer> result = new ArrayList<>();
        if (root == null) return result;
        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(root);
        while (!queue.isEmpty()) {
            int size = queue.size();
            for (int i = 0; i < size; i++) {
                TreeNode node = queue.poll();
                if (i == size - 1) result.add(node.val);
                if (node.left != null) queue.offer(node.left);
                if (node.right != null) queue.offer(node.right);
            }
        }
        return result;
    }
}

Edge Cases:

  • Empty tree
  • Single node
  • Left skewed tree
Binary Tree Zigzag Level Order Traversal
BFS with Direction Flag

Return the zigzag level order traversal (left to right, then right to left, etc.).

Example:

Input: root = [3,9,20,null,null,15,7]

Output: [[3],[20,9],[15,7]]

Level 0 L-R, Level 1 R-L, Level 2 L-R.

Optimal Solution — O(n) time, O(n) space

BFS with alternating direction

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 size = queue.size();
            LinkedList<Integer> level = new LinkedList<>();
            for (int i = 0; i < size; i++) {
                TreeNode node = queue.poll();
                if (leftToRight) level.addLast(node.val);
                else level.addFirst(node.val);
                if (node.left != null) queue.offer(node.left);
                if (node.right != null) queue.offer(node.right);
            }
            result.add(level);
            leftToRight = !leftToRight;
        }
        return result;
    }
}

Edge Cases:

  • Single node
  • Empty tree
  • Perfect binary tree
Boundary of Binary Tree
Traversal Decomposition

The boundary of a binary tree is the concatenation of the root, the left boundary, the leaves from left-to-right, and the reverse of the right boundary.

Example:

Input: root = [1,null,2,3,4]

Output: [1,3,4,2]

Root -> left boundary -> leaves -> right boundary.

Optimal Solution — O(n) time, O(h) space

Three passes: left boundary, leaves, right boundary

class Solution {
    public List<Integer> boundaryOfBinaryTree(TreeNode root) {
        List<Integer> result = new ArrayList<>();
        if (root == null) return result;
        result.add(root.val);
        addLeftBoundary(root.left, result);
        addLeaves(root.left, result);
        addLeaves(root.right, result);
        addRightBoundary(root.right, result);
        return result;
    }
    private void addLeftBoundary(TreeNode node, List<Integer> result) {
        while (node != null) {
            if (node.left != null || node.right != null) result.add(node.val);
            node = node.left != null ? node.left : node.right;
        }
    }
    private void addLeaves(TreeNode node, List<Integer> result) {
        if (node == null) return;
        if (node.left == null && node.right == null) { result.add(node.val); return; }
        addLeaves(node.left, result);
        addLeaves(node.right, result);
    }
    private void addRightBoundary(TreeNode node, List<Integer> result) {
        List<Integer> temp = new ArrayList<>();
        while (node != null) {
            if (node.left != null || node.right != null) temp.add(node.val);
            node = node.right != null ? node.right : node.left;
        }
        for (int i = temp.size() - 1; i >= 0; i--) result.add(temp.get(i));
    }
}

Edge Cases:

  • Single node
  • Only left children
  • Only right children

Quiz

1. Which traversal uses a stack data structure for iterative implementation?

Question 1 options

2. What is the space complexity of Morris traversal?

Question 2 options

3. What is a common mistake when implementing Tree Traversals?

Question 3 options

Flashcards

Question

What traversal order does Morris traversal achieve?

Answer

In-order traversal (Left → Root → Right) in O(1) space by threading the tree. It temporarily modifies the tree structure then restores it.

Question

How do you implement BFS level-order traversal?

Answer

Use a Queue. Process level by tracking queue size at start of each level. Add children to queue during processing. O(n) time, O(w) space where w is max width.

Question

Tree Traversals best practices

Answer

Follow SOLID principles, write clean code, test thoroughly, document decisions, and monitor in production.

Revision Notes

Key Takeaways

  • 1. Pre-order: process root first, use for copying/serializing trees
  • 2. In-order: process root between children, gives sorted order in BST
  • 3. Post-order: process root last, use for deleting/computing height
  • 4. BFS: process level-by-level, use for shortest path/level problems

Interview Tips

  • Know all three DFS orders both recursively and iteratively
  • BFS is preferred for level-based problems and shortest path
  • Morris traversal is impressive but know when to mention it
  • For iterative post-order, remember the two-stack trick or use one stack with reverse pre-order

Cheat Sheet

Tree Traversals Cheat Sheet

DFS Orders:

  • Pre-order: Root → Left → Right (stack: push right then left)
  • In-order: Left → Root → Right (traverse leftmost, process, go right)
  • Post-order: Left → Right → Root (two-stack or reverse pre-order)

Iterative In-order Template:

Deque<TreeNode> stack = new ArrayDeque<>();
TreeNode curr = root;
while (curr != null || !stack.isEmpty()) {
    while (curr != null) {
        stack.push(curr);
        curr = curr.left;
    }
    curr = stack.pop();
    process(curr.val);
    curr = curr.right;
}

BFS Level-Order Template:

Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
while (!queue.isEmpty()) {
    int size = queue.size();
    for (int i = 0; i < size; i++) {
        TreeNode node = queue.poll();
        // process node
        if (node.left != null) queue.offer(node.left);
        if (node.right != null) queue.offer(node.right);
    }
}

Morris In-order (O(1) space):

  1. If no left child: visit, go right
  2. If left child: find predecessor
  3. If predecessor.right is null: thread it, go left
  4. If predecessor.right is current: unthread, visit, go right

Complexity:

Method Time Space
Recursive O(n) O(h)
Iterative O(n) O(h)
Morris O(n) O(1)