AlgoMaster Logo

Binary Tree Preorder Traversal

easyFrequency6 min readUpdated June 23, 2026

Understanding the Problem

We need to traverse a binary tree in preorder and return the node values as a list. Preorder traversal follows a specific visitation order: process the current node first, then recursively traverse the left subtree, then recursively traverse the right subtree. The name "preorder" comes from the fact that the node is processed before its children.

This is one of the three fundamental depth-first tree traversal patterns (preorder, inorder, postorder). While the recursive solution is straightforward, the real learning value here is understanding how to convert recursion into an iterative approach using an explicit stack, and then going further with Morris traversal to eliminate even the stack.

Recursion uses the call stack implicitly. Avoiding recursion means managing that stack ourselves, and reaching O(1) space means replacing the stack with temporary links stored in the tree itself.

Key Constraints:

  • Number of nodes in range [0, 100] → With at most 100 nodes, any approach works. The focus here is on understanding traversal techniques, not optimizing for large inputs.
  • -100 <= Node.val <= 100 → Node values can be negative. This doesn't affect traversal logic.
  • The tree can be empty (0 nodes), so we need to handle the null root case.

Approach 1: Recursive DFS

Intuition

The definition of preorder traversal is itself recursive: visit the current node, then preorder-traverse the left subtree, then preorder-traverse the right subtree. Translating that definition directly into code gives a short recursive function. The call stack tracks where to resume after each recursive call returns, so the function needs no explicit bookkeeping of its own.

Algorithm

  1. If the root is null, return an empty list.
  2. Create an empty result list.
  3. Define a recursive helper function preorder(node):
    • If node is null, return.
    • Add node.val to the result list.
    • Recurse on node.left.
    • Recurse on node.right.
  4. Call preorder(root) and return the result.

Example Walkthrough

root
1Start: call preorder(1)
1current23
result
1Result is empty
1/4

Code

The call stack has a fixed size limit, and a deeply skewed tree can overflow it. The next approach moves the stack into a data structure we allocate and control ourselves.

Approach 2: Iterative with Stack

Intuition

The recursive solution uses the call stack to remember where to resume after each subtree. An explicit stack of our own does the same job. Preorder processes the current node first, then the left subtree, then the right subtree. Because a stack is LIFO (last in, first out), pushing the right child before the left child leaves the left child on top: the left subtree is processed first while the right child waits on the stack, which produces the order node, left subtree, right subtree.

Algorithm

  1. If the root is null, return an empty list.
  2. Create an empty result list and a stack. Push the root onto the stack.
  3. While the stack is not empty:
    • Pop a node from the stack.
    • Add its value to the result list.
    • If the node has a right child, push it onto the stack.
    • If the node has a left child, push it onto the stack.
  4. Return the result.

Example Walkthrough

root
1Start: push root (1) onto stack
1pop2453
stack
1Push root (1)
1
Top
result
1Result is empty
1/6

Code

Both the recursive and iterative approaches use O(h) extra space. Morris traversal removes the stack entirely by storing the return path inside the tree's own unused pointers.

Approach 3: Morris Traversal (Optimal Space)

Intuition

Morris traversal achieves O(1) extra space by temporarily modifying the tree. Before entering a node's left subtree, it creates a temporary link (a "thread") from the rightmost node of that left subtree back to the current node. After the left subtree finishes, the traversal follows the thread back to the current node and continues into the right subtree. The thread replaces the stack: the information about where to return is stored in the tree itself.

For preorder specifically, the current node is added to the result on the first encounter, before going left. On the second encounter, reached by following the thread, the thread is removed and nothing is added.

Algorithm

  1. Initialize current to the root and create an empty result list.
  2. While current is not null:
    • If current has no left child: add current.val to the result, move to current.right.
    • If current has a left child: find the inorder predecessor (rightmost node in left subtree).
      • If predecessor's right is null (first visit): create thread, add current.val to result, move left.
      • If predecessor's right is current (second visit): remove thread, move right.
  3. Return the result.

Example Walkthrough

root
1current=1, has left child. Find predecessor (5). Create thread 5->1. Visit 1
1current245predecessor3
result
1Visit 1
1
1/7

Code