Trees · Binary Tree Fundamentals

Binary Tree DFS Traversal Orders

Visiting every node exactly once via recursion, where only the order of three fixed steps - visit the node, recurse left, recurse right - changes between inorder, preorder, and postorder, and the same recursive shape finds a root-to-node path by remembering the current path and backtracking on failed branches.

  • 6 min read
  • 10 practice questions
  • 5 graded problems
On this page
  1. Description
  2. The approach: the three traversal orders
  3. A second flavor: the path from the root to a node
  4. Where you'll see it
  5. Which tool for which job?
  6. Common mistakes
  7. Check yourself
  8. Key takeaways
  9. Quick revise
  10. Practice

Description

Depth-first traversal visits a node and both its subtrees. The three classic orders differ only in when the node itself is handled: before its subtrees (preorder), between them (inorder) or after them (postorder). Each order fits a job. Preorder copies or serializes a tree, inorder reads a BST in sorted order, and postorder deletes or evaluates it. Add a path that grows on the way down and shrinks on the way back, and the same walk finds the route from the root to any node.

We'll go from simple to more technical:

  1. The pattern: preorder, inorder and postorder as one recursion.
  2. A second flavor: the root-to-node path, with backtracking.
  3. Which order fits which production job.
  4. The bugs: wrong order, missing backtrack, and comparing two trees safely.

The approach: the three traversal orders

At every node there are three things to do: handle the node, walk its left subtree and walk its right subtree. The subtrees always go left before right. The only choice is where the node goes in that sequence.

4

2

7

1

3

4

2

7

1

3

OrderWhen the node is handledVisits
Preorderbefore its subtrees4 2 1 3 7
Inorderbetween its subtrees1 2 3 4 7
Postorderafter its subtrees1 3 2 7 4

This tree is a BST, so inorder gives its values in sorted order.

Java

void inorder(TreeNode n)   { if (n==null) return; inorder(n.left);  visit(n); inorder(n.right); }
void preorder(TreeNode n)  { if (n==null) return; visit(n); preorder(n.left);  preorder(n.right); }
void postorder(TreeNode n) { if (n==null) return; postorder(n.left); postorder(n.right); visit(n); }

Python

def inorder(n: TreeNode | None) -> None:
    if n is None:
        return
    inorder(n.left)
    visit(n)
    inorder(n.right)


def preorder(n: TreeNode | None) -> None:
    if n is None:
        return
    visit(n)
    preorder(n.left)
    preorder(n.right)


def postorder(n: TreeNode | None) -> None:
    if n is None:
        return
    postorder(n.left)
    postorder(n.right)
    visit(n)

A second flavor: the path from the root to a node

To find the route to a node, keep a list path. Add each node when you arrive. If neither subtree leads to the target, remove the node again before returning. Searching for 3 in the tree above: add 4, add 2, add 1. Node 1 is a dead end, so remove it. Then add 3: found, and path is [4, 2, 3].

Java

boolean find(TreeNode node, int target, List<Integer> path) {
    if (node == null) return false;
    path.add(node.val);
    if (node.val == target) return true;
    if (find(node.left, target, path) || find(node.right, target, path)) return true;
    path.remove(path.size() - 1);   // this branch didn't lead to the target - undo it
    return false;
}

Python

def find(node: TreeNode | None, target: int, path: list[int]) -> bool:
    if node is None:
        return False
    path.append(node.val)
    if node.val == target:
        return True
    if find(node.left, target, path) or find(node.right, target, path):
        return True
    path.pop()   # this branch didn't lead to the target - undo it
    return False

Add on the way in, remove on a dead end. That keeps path equal to the route to wherever the search is right now. It's the same push-then-pop shape as backtracking in Backtracking: Subsets & Choices.

Where you'll see it

Sorted sets and maps built on search trees list their contents with an inorder walk. Deleting a tree safely is postorder: children before the parent. Saving a tree is often preorder, because the reader learns each node before the subtrees it has to build under it. Finding one file's full path in a folder tree is the root-to-node search.

Which tool for which job?

You needUse
Copy or serialize a treepreorder (root first)
A BST's values in sorted orderinorder
Delete a tree, or evaluate an expression treepostorder (children first)
The path from the root to a nodeDFS with a path you push and pop
Compare two treesrecurse on both together, nulls first
No recursionan explicit stack (postorder: reverse root-right-left)

Common mistakes

MistakeWhat happensDo this instead
Freeing a node before its childrenchildren become unreachablepostorder for deletion
No pop after a failed branchthe path keeps dead-end nodesremove the node before returning
Reading .val before checking both nodes for nullNullPointerExceptionif p == null or q == null: return p == q
Copying the path at every nodeO(n x h) copiesone list, push and pop
Pushing null children onto a Java ArrayDequeNullPointerExceptionpush only non-null children

Check yourself

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

1. Why does deleting a tree need postorder rather than preorder?

Show answer

If you delete a node first, you lose your only way to reach its children. In a garbage-collected language they're orphaned; in manual memory management you read freed memory. Postorder handles both children first, so deleting the parent last never needs anything that's gone.

2. The search finds the target in the left subtree. Does find(node.left, ...) || find(node.right, ...) still search the right subtree?

Show answer

No. || stops as soon as the left call returns true. With a unique target, searching the right side anyway would only waste time. With a duplicate value, it would add a second route to the path, so stopping at the first match keeps the path meaningful.

3. A same-tree check compares p.val == q.val before checking either node for null. What breaks?

Show answer

When exactly one of them is null, reading .val from it crashes before the code can report "different". Handle the null cases first: both null means equal, and one null means different. Only then compare values.

Key takeaways

  • Preorder, inorder and postorder differ only in when the node is handled: before, between or after its subtrees.
  • Preorder copies and serializes, inorder sorts a BST, postorder deletes and evaluates.
  • For a root-to-node path, add the node on the way in and remove it on a dead end.
  • || stops at the first match, which keeps the path to a single route.
  • When walking two trees at once, check for nulls before reading any value.

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
Copy / serializepreorder
Sorted BST valuesinorder
Delete / evaluatepostorder
Root-to-node pathDFS + push/pop
No recursionan explicit stack

Snippets worth knowing by heart

Java

Deque<TreeNode> stack = new ArrayDeque<>();
TreeNode node = root;
while (node != null || !stack.isEmpty()) {
    while (node != null) { stack.push(node); node = node.left; }   // all the way left
    node = stack.pop();
    out.add(node.val);                                             // inorder visit
    node = node.right;
}

Python

def find_path(node, target, path):
    if node is None:
        return False
    path.append(node.val)
    if node.val == target or find_path(node.left, target, path) or find_path(node.right, target, path):
        return True
    path.pop()                       # backtrack out of a dead end
    return False

The traps

  • The wrong order for the job: freeing a node before its children loses them. Use postorder for deletion.
  • No backtrack: the path keeps dead-end nodes. Remove the node before returning.
  • Null checks when comparing two trees: reading .val first crashes. Return p == q when either is null.
  • Copying the path at every node: that's O(n x h) copies. Keep one list and push and pop.
  • null in an ArrayDeque: Java throws a NullPointerException. Push only non-null children.

Staff-level points, one line each

  • Iterative postorder = reversed root-right-left; matched recursion on 300 trees.
  • Preorder [1, 2] fits two trees; null markers tell them apart.
  • Path copying on 800 nodes: 320,400 element copies vs 800 appends.
  • ArrayDeque.push(null) throws NullPointerException.
  • Two trees: if p is None or q is None: return p is q.

Say it in an interview

  • "The three orders differ only in when I visit the node relative to its subtrees."
  • "Inorder on a BST gives sorted order."
  • "I push the node on the way down and pop it if that branch fails."
  • "Iteratively, I use a stack - same O(h) space, but no recursion limit."

Practice

This skill comes with 10 multiple-choice questions that tell you which misconception a wrong answer reveals and 5 graded problems. 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 Binary Tree DFS Traversal Orders free

Same Tree

EasyFreeCoding problemJava · Python

Two binary trees are the same when they have the same shape and every pair of matching nodes holds the same value. Given two trees, return whether they are the same.

Print Root to Node Path

MediumProCoding problemJava · Python

Given the root of a binary tree and a target value guaranteed to exist in it, return the sequence of node values from the root to the node with that value.

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

Practice Binary Tree DFS Traversal Orders free

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

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