Trees · Binary Tree Fundamentals

Single-Pass Triple Traversal

Producing preorder, inorder, and postorder simultaneously from one iterative pass, by tracking how many times each node has been returned to on an explicit stack - the first visit emits preorder, the second inorder, the third postorder.

  • 6 min read
  • 11 practice questions
  • 1 graded problem
On this page
  1. Description
  2. The approach
  3. Where you'll see it
  4. Which tool for which job?
  5. Common mistakes
  6. Check yourself
  7. Key takeaways
  8. Quick revise
  9. Practice

Description

Single-pass triple traversal produces preorder, inorder and postorder together, in one walk with an explicit stack. Each stack frame remembers which of its three visits the node is on. The first time it's on top, record preorder and go left. The second time, record inorder and go right. The third time, record postorder and pop. It's also the general recipe for turning any recursion into a loop: a frame that knows where it is.

We'll go from simple to more technical:

  1. The pattern: a stack of (node, phase) frames.
  2. Where getting several views in one walk helps.
  3. The bugs: state that doesn't stick, and updating the wrong frame.
  4. What it saves - and what it doesn't.

The approach

You could run three separate traversals. But in recursion, control comes back to each node exactly three times: before its left subtree, between its subtrees, and after them. Those three moments are exactly preorder, inorder and postorder. So keep each node on the stack with a counter of which visit it's on, and record the node in a different list each time.

4

2

7

1

4

2

7

1

Top of the stackVisitRecordsThen
41stpreorder: 4push 2
21stpreorder: 2push 1
11stpreorder: 1no left child
12ndinorder: 1no right child
13rdpostorder: 1pop
22ndinorder: 2no right child
23rdpostorder: 2pop
42ndinorder: 4push 7
71stpreorder: 7no left child
72ndinorder: 7no right child
73rdpostorder: 7pop
43rdpostorder: 4pop

The results are preorder 4 2 1 7, inorder 1 2 4 7 and postorder 1 2 7 4.

Java

Deque<Object[]> stack = new ArrayDeque<>();  // {TreeNode, Integer visitCount}
if (root != null) stack.push(new Object[]{root, 1});
while (!stack.isEmpty()) {
    Object[] top = stack.peek();
    TreeNode node = (TreeNode) top[0];
    int count = (int) top[1];
    if (count == 1) {
        pre.add(node.val);
        top[1] = 2;
        if (node.left != null) stack.push(new Object[]{node.left, 1});
    } else if (count == 2) {
        in.add(node.val);
        top[1] = 3;
        if (node.right != null) stack.push(new Object[]{node.right, 1});
    } else {
        post.add(node.val);
        stack.pop();
    }
}

Python

stack = [[root, 1]] if root else []    # [node, visit_count] - a list, so the count changes in place
while stack:
    top = stack[-1]
    node, count = top
    if count == 1:
        pre.append(node.val)
        top[1] = 2
        if node.left:
            stack.append([node.left, 1])
    elif count == 2:
        inorder.append(node.val)
        top[1] = 3
        if node.right:
            stack.append([node.right, 1])
    else:
        post.append(node.val)
        stack.pop()

A node's frame stays on the stack for all three visits; only its counter changes. It's popped after the third visit, once both children are done.

Where you'll see it

The bigger lesson is the frame itself. Any recursion can become a loop this way: each frame stores its own "where was I", and the loop resumes it from there. That's how you avoid stack overflows on very deep trees, and how tree iterators that yield one node at a time are built.

Which tool for which job?

You needUse
All three orders at onceone stack of (node, phase) frames
One order, iterativelythat order's own stack loop
Any recursion as a loopframes that store their own "where was I"
A lazy in-order iteratora stack of left spines (or a generator on shallow trees)
Recursion is fine (shallow trees)just recurse - simplest

Common mistakes

MistakeWhat happensDo this instead
Frames as Python tuplesthe phase update is lost; it loops foreverlists, or a small mutable class
Updating the phase through peek() after pushing a childthe child's phase changes; it loopskeep a reference to the parent's own frame
Pushing a null childNullPointerException / None framespush only real children
Popping in phase 2postorder is never recordedpop only in phase 3
Expecting a 3x speed-upit's still 3 loop iterations per nodethe saving is pushes and pops

Check yourself

Try to answer before opening each one - recalling beats re-reading.

1. A version reads the top frame into local variables, then pushes a new frame with the next count instead of updating the old one. What goes wrong?

Show answer

The old frame is still on the stack with its old count. When control comes back to it, the node is handled as if this were the earlier visit again, and the walk repeats work or loops. The technique needs the same frame, updated in place, to be found again later.

2. After pushing the left child, a version sets the parent's count with stack.peek()[1] = 2. What goes wrong?

Show answer

After the push, peek() returns the child's frame. So the child jumps to its second visit and never appears in preorder, while the parent stays on its first visit. When the child is popped, the parent pushes its left child again, forever. Update the parent through top, the reference you already hold.

3. Why does a node's frame stay on the stack for all three visits instead of being popped and pushed again?

Show answer

Popping would throw away the one fact the technique needs: which visit this is. One frame with a counter that changes in place answers "how many times have I been here" with no extra lookup.

Key takeaways

  • Control returns to each node three times. Those three moments are preorder, inorder and postorder.
  • Update a frame in place. A copy loses the visit count.
  • After pushing a child, update the parent through your own reference to its frame, not through peek().
  • A node is popped only after its third visit.
  • It's still O(n) with three loop steps per node. The saving is fewer pushes and pops, not two-thirds of the work.

Quick revise

The whole skill on one page, for the night before an interview.

Everything from the lesson and the deep dive on one page. Skim it before practice or an interview, and open the full lesson for anything that doesn't ring a bell.

Pick the tool

You needUse
All three orders(node, phase) frames
Recursion → loopframes with a resume point
A lazy in-order iteratora stack of left spines
Shallow treesplain recursion

Snippets worth knowing by heart

Java

Deque<Frame> stack = new ArrayDeque<>(List.of(new Frame(root)));
while (!stack.isEmpty()) {
    Frame top = stack.peek();                        // keep this reference
    if (top.phase == 1) { pre.add(top.node.val); top.phase = 2;
        if (top.node.left != null) stack.push(new Frame(top.node.left)); }
    else if (top.phase == 2) { in.add(top.node.val); top.phase = 3;
        if (top.node.right != null) stack.push(new Frame(top.node.right)); }
    else { post.add(top.node.val); stack.pop(); }
}

Python

stack = [[root, 1]]                  # lists, not tuples: the phase must change in place
while stack:
    frame = stack[-1]
    node, phase = frame
    if phase == 1:
        pre.append(node.val); frame[1] = 2
        if node.left: stack.append([node.left, 1])
    elif phase == 2:
        ino.append(node.val); frame[1] = 3
        if node.right: stack.append([node.right, 1])
    else:
        post.append(node.val); stack.pop()

The traps

  • Tuple (or record) frames: the phase update is lost and it loops forever. Use lists, or a small mutable class.
  • Re-peeking after a push: you change the child's phase, not the parent's. Keep a reference to the parent's frame.
  • Null children pushed: you crash or get None frames. Push only real children.
  • Popping early: popping in phase 2 never records postorder. Pop only in phase 3.
  • "Three times faster": it's still three loop iterations per node. The saving is pushes and pops.

Staff-level points, one line each

  • A frame = node + resume point: the general recursion-to-loop recipe.
  • 1,023 nodes: 3,069 loop iterations, 1,023 pushes.
  • Tuple frames looped until the cap; re-peeking after a push did too.
  • Nested generators cost O(h) per item - a 3,000-node chain was slower than a 4,095-node balanced tree.

Say it in an interview

  • "Each frame remembers which visit this is, like a return address."
  • "First visit: preorder and go left; second: inorder and go right; third: postorder and pop."
  • "It's still O(n) - three steps per node - but one walk."
  • "I keep a reference to the node's own frame, so pushing a child doesn't confuse it."

Practice

This skill comes with 11 multiple-choice questions that tell you which misconception a wrong answer reveals and 1 graded problem. Your answers are graded and your mastery is tracked, so you'll know when the skill holds up. Running code and saving progress need a free account.

Practice Single-Pass Triple Traversal free

Preorder, Inorder, and Postorder in a Single Traversal

HardProCoding problemJava · Python

Given the root of a binary tree, compute its preorder, inorder, and postorder traversals - all three - using a single iterative pass (one stack, one loop), not three separate traversals.

The full statement, the examples and the hidden test suite are part of AI Proof Engineers Pro.

Practice Single-Pass Triple Traversal free

No card needed. Lessons, practice questions and easy problems are free forever.

Free plan, no card · 7-day Pro trial · Cancel anytime