Trees & Graphs
Beginner 9 min read

Tree traversals

In one line

A traversal is a fixed order for visiting every node of a tree once: preorder, inorder and postorder go deep first, level order goes row by row.

What it is

A binary tree is a set of nodes where each node has a value and at most two children, left and right. The top node is the root; nodes with no children are leaves. A traversal visits every node exactly once in a defined order.

There are four orders interviewers expect. Preorder visits a node before its children (node, left, right). Inorder visits the left subtree, then the node, then the right subtree. Postorder visits both children before the node (left, right, node). Level order (breadth-first) visits the root, then every node at depth 1, then depth 2, and so on.

The first three are depth-first and are naturally written with recursion or an explicit stack. Level order uses a queue. Every traversal takes O(n) time; depth-first ones use O(h) extra space where h is the tree height, and level order uses O(w) where w is the widest level.

Why interviewers ask

Almost every tree question is a traversal with some work attached: serialize a tree, print it by levels, check if it is a valid BST, find the right side view. Interviewers check that you know which order fits which job (inorder of a BST is sorted, postorder lets children report to parents, level order handles anything about depth) and that you can write the iterative versions when recursion is not allowed.

How it works

  1. 1Pick the order based on when the node's own work must happen: before its children (preorder), between them (inorder), or after them (postorder).
  2. 2Recursive depth-first traversal: if the node is empty, return. Otherwise make two recursive calls (left, then right) and do the node's work at the chosen point.
  3. 3Iterative inorder: push nodes while walking left; when you cannot go left, pop a node, visit it, and move to its right child. Repeat until the stack is empty and the current node is empty.
  4. 4Iterative preorder: push the root; pop a node, visit it, push its right child then its left child so the left is processed first.
  5. 5Level order: put the root in a queue. While the queue is not empty, record its current size k, pop exactly k nodes (that is one level) and push their children for the next level.

Worked example · All four orders on a five-node tree

  1. Tree: root 1 with left child 2 and right child 3. Node 2 has left child 4 and right child 5. Node 3 is a leaf.
  2. Preorder (node, left, right): visit 1, go left to 2, visit 2, go to 4, visit 4, back to 2's right 5, visit 5, then 3. Result: 1 2 4 5 3.
  3. Inorder (left, node, right): go left from 1 to 2 to 4. 4 has no left, so visit 4, then its parent 2, then 2's right child 5, then 1, then 3. Result: 4 2 5 1 3.
  4. Postorder (left, right, node): 4 and 5 finish first, then their parent 2, then 3, and the root last. Result: 4 5 2 3 1.
  5. Level order: queue [1] gives level [1] and queue becomes [2, 3]. Pop both: level [2, 3], queue [4, 5]. Pop both: level [4, 5], queue empty. Result: [[1], [2, 3], [4, 5]].

Picture it

Think of reading a company org chart. Preorder is a manager introducing themselves before their team; postorder is a manager who first collects reports from every team member and then summarises; level order is a group photo taken one row at a time: executives, then directors, then everyone else.

Reference code

from collections import deque

def preorder(node, out):              # fills out in place
    if node:
        out.append(node.val)          # node first
        preorder(node.left, out)
        preorder(node.right, out)

def inorder_iterative(root):
    out, stack, node = [], [], root
    while stack or node:
        while node:                   # go as far left as possible
            stack.append(node)
            node = node.left
        node = stack.pop()
        out.append(node.val)
        node = node.right
    return out

def level_order(root):
    levels, q = [], deque([root] if root else [])
    while q:
        level = []
        for _ in range(len(q)):       # exactly the nodes of this level
            node = q.popleft()
            level.append(node.val)
            if node.left: q.append(node.left)
            if node.right: q.append(node.right)
        levels.append(level)
    return levels

Nodes have val, left and right fields (see the TreeNode class on the BST page). Recursive preorder shows the shape every depth-first traversal shares; moving the append gives inorder or postorder. The iterative inorder uses a stack to replace the call stack. Level order snapshots the queue size so each inner loop handles exactly one level. All run in O(n) time.

Key patterns

Inorder for sorted order

When: The tree is a BST and you need values in sorted order, the k-th smallest, or to validate the BST

How: Do an inorder walk; values must come out strictly increasing. Stop early once you reach the k-th visit.

Postorder to combine children

When: A node's answer depends on its subtrees (height, size, sums, deleting a tree)

How: Recurse into both children first, then combine their returned values at the node.

Level order by size snapshot

When: Anything per level: right side view, level averages, zigzag order, minimum depth

How: At the start of each level read len(queue) and pop exactly that many nodes.

Preorder with markers to serialize

When: Serialize and deserialize a tree, or compare tree shapes

How: Write each value in preorder and a marker such as # for every empty child, then rebuild by reading the stream in the same order.

Real-world use

File system tools

Commands like du compute a folder's size in postorder: each subfolder's size must be known before the parent can add them up. rm -r also deletes contents before the folder itself.

Browsers and the DOM

Browsers walk the DOM tree depth-first in document order (a preorder) for things like querySelectorAll results and event propagation paths, so elements come back in the order they appear in the HTML.

Compilers and expression trees

A compiler turns code into an abstract syntax tree. Evaluating or generating code for an expression is a postorder walk (operands before the operator); printing it back with parentheses is an inorder walk.

Serializing trees

Saving a tree to a file or sending it over the network often uses preorder with markers for empty children, because the root comes first and the tree can be rebuilt in one pass.

In a project

You build a comment-thread viewer for a forum: each comment can have replies, and the app must show threads indented, count replies per comment, and load the first few levels quickly on mobile.

  1. Store comments as a tree (each comment has a list of children) built from parent_id in one pass.
  2. Render with a preorder walk, passing depth down so each comment is indented under its parent.
  3. Compute reply counts with a postorder walk: each comment's count is the sum of its children's counts plus the number of children.
  4. For the mobile first screen, use level order and stop after depth 2, then lazy-load deeper replies when the user taps.
  5. Use an explicit stack instead of recursion so a very deep reply chain cannot overflow the call stack.

Impact: Rendering and counting each take one O(n) pass instead of repeated database queries per comment, and the first screen loads only the top levels, cutting payload size for long threads.

Trade-offs

Use it when

  • Preorder: you need the parent before the children (copying, serializing, passing depth or path down)
  • Inorder: the tree is a BST and order of values matters
  • Postorder: the parent needs results from its children (heights, sizes, freeing memory)
  • Level order: the question mentions levels, depth, nearest, or the view from one side

Avoid it when

  • Recursion on a very deep or skewed tree (say 100,000 nodes in a chain): Python's default recursion limit is about 1000, so use an explicit stack
  • Level order on a very wide, complete tree when memory is tight: the last level holds about n/2 nodes in the queue
  • Inorder on a general binary tree expecting sorted output: that only holds for BSTs

Cost: All traversals are O(n) time. Depth-first uses O(h) space (O(log n) balanced, O(n) skewed); level order uses O(w), up to about n/2 for a complete tree.

Common mistakes

  • Mixing up the orders: the name tells you where the node goes relative to its children (pre = before, in = between, post = after).
  • Forgetting the base case for an empty node, which causes errors on leaves' missing children.
  • In level order, using a loop over the live queue instead of a size snapshot, so levels bleed into each other.
  • Pushing the left child before the right in iterative preorder, which visits the right subtree first.
  • Assuming recursion space is O(1): the call stack costs O(h), which is O(n) for a skewed tree.

Interview questions

Quick revision

  • Preorder: node, left, right. Inorder: left, node, right. Postorder: left, right, node.
  • Level order uses a queue; read len(queue) to process one level at a time.
  • Inorder of a BST is sorted.
  • Postorder when the parent needs children's results (height, size).
  • Every traversal is O(n) time; DFS space O(h), BFS space O(w).
  • Iterative inorder: push left chain, pop and visit, go right.
  • Iterative preorder: pop and visit, push right then left.

Check yourself

1. For the tree with root 1, left child 2 (children 4 and 5) and right child 3, what is the postorder?

2. Which traversal outputs the values of a binary search tree in sorted order?

3. What data structure does level order traversal use?

4. What is the extra space of recursive inorder on a tree that is a single chain of n nodes?

Saved on this device. Create a free account to keep it on every device.

Practise this with Sam

A real mock interview, out loud, with an honest scorecard. Free to start.

Start free