Learn/DSA/Tree Traversals
AlgorithmsIntermediate9 min

Tree Traversals

Depth-first (pre/in/post-order) and breadth-first (level-order) ways to visit every node — recursive and iterative.

TreesDFSBFSRecursion

A traversal is a systematic way to visit every node in a tree exactly once. Because a tree branches, there is no single "next" node — you must decide, at each step, whether to go deep (follow a child all the way down) or go wide (finish the current level before descending).

That single decision splits traversals into two families: depth-first search (DFS), which plunges down one branch before backtracking, and breadth-first search (BFS), which sweeps level by level. DFS naturally uses a stack (or the call stack via recursion); BFS uses a queue.

Tree Traversals
1234567
visit order:
Inorder: Left → Node → Right (sorted for a BST)
Watch pre-order, in-order, post-order, and level-order visit the same tree in four different orders.

The three DFS orders

DFS on a binary tree comes in three flavours, distinguished only by when you process the current node relative to its subtrees:

  • Pre-order (node → left → right): process the node first. Great for copying a tree or serialising its structure top-down.
  • In-order (left → node → right): process the node between its subtrees. On a binary search tree this yields the keys in sorted order.
  • Post-order (left → right → node): process the node last. Ideal for deleting or freeing a tree, since you handle children before the parent.
Recursive DFS — all three orders
function preorder(node, out = []) {
  if (!node) return out
  out.push(node.val)          // node
  preorder(node.left, out)    // left
  preorder(node.right, out)   // right
  return out
}

function inorder(node, out = []) {
  if (!node) return out
  inorder(node.left, out)     // left
  out.push(node.val)          // node
  inorder(node.right, out)    // right
  return out
}

function postorder(node, out = []) {
  if (!node) return out
  postorder(node.left, out)   // left
  postorder(node.right, out)  // right
  out.push(node.val)          // node
  return out
}

The BST superpower

In-order traversal of a binary search tree emits the values in ascending order — no sorting step needed. If you ever need the k-th smallest key or a sorted dump of a BST, in-order is the answer.

Doing DFS without recursion

Recursion is really just an implicit stack. When the tree is deep enough to risk a stack-overflow (or in languages without tail calls), swap in an explicit stack. Pre-order is the cleanest to convert: push right before left so left is popped first.

Iterative pre-order with an explicit stack
function preorderIterative(root) {
  if (!root) return []
  const out = [], stack = [root]
  while (stack.length) {
    const node = stack.pop()
    out.push(node.val)
    // push right first so left is processed next
    if (node.right) stack.push(node.right)
    if (node.left) stack.push(node.left)
  }
  return out
}

Level-order (BFS)

To visit nodes level by level, use a queue: dequeue a node, process it, then enqueue its children. Tracking the queue length at the start of each round lets you group nodes by level — the standard trick for "return values level by level" problems.

Level-order traversal with a queue
function levelOrder(root) {
  if (!root) return []
  const out = [], queue = [root]
  while (queue.length) {
    const level = []
    for (let n = queue.length; n > 0; n--) {
      const node = queue.shift()
      level.push(node.val)
      if (node.left) queue.push(node.left)
      if (node.right) queue.push(node.right)
    }
    out.push(level)
  }
  return out
}
OperationTime

n = number of nodes, h = height of the tree. All orders visit each node once.

Which one do I pick?

Reach for BFS when the answer depends on distance from the root (shallowest node, minimum depth, level grouping). Reach for DFS for structural work (path sums, subtree properties, serialisation) where recursion mirrors the shape of the problem.

Section navigation