Binary Tree Traversals: Preorder, Inorder & Postorder Explained
Understand the three fundamental depth-first traversals using recursion, visual examples, and C++ & Java implementations.

When working with a binary tree, the real question isn't just how to move through its nodes — it's in what order you choose to visit them.
There are three fundamental depth-first search (DFS) traversals:
Preorder
Inorder
Postorder
All three share the exact same recursive skeleton. The only thing that changes between them is when the current node gets processed relative to its subtrees.
Table of Contents
The Basic Rule
Before comparing the three traversals, internalize one rule that never changes:
We always move left to right — left subtree before right subtree.
The only variable is where the current node is processed relative to that left-to-right movement:
Preorder → Root → Left → Right
Inorder → Left → Root → Right
Postorder → Left → Right → Root
Once you see where the root falls in each pattern, all three become trivial to derive on the spot — no memorization required.
We'll use this tree as a running example throughout:
1
/ \
2 3
/ \
4 5
Preorder Traversal
Order: Root → Left → Right
At each node:
Visit the current node.
Traverse the left subtree.
Traverse the right subtree.
Walkthrough:
Start at
1— it's the root, so process it first.Descend into the left subtree rooted at
2. Process2, then its left child4(no children → return), then its right child5.With the entire left subtree of
1finished, move to the right subtree,3.
Result:
1 → 2 → 4 → 5 → 3
The Recursion Intuition
This is the part that makes traversals click.
Every time recursion descends into a child, that child becomes the root of a smaller subtree — and the exact same rule (Root → Left → Right) applies again, just at a smaller scale.
1
/
2
/
4
When recursion reaches 2, it doesn't need new logic — it just re-applies Root → Left → Right:
2 → 4 → ...
And at 4, both children are null, so the call returns immediately and control unwinds back up the call stack. This self-similar structure is exactly why recursion maps so naturally onto trees.
Preorder — C++
class Solution {
public:
void preOrder(TreeNode* root, vector<int>& ans) {
if (root == nullptr)
return;
ans.push_back(root->val); // process root first
preOrder(root->left, ans);
preOrder(root->right, ans);
}
};
vector<int>& ans is passed by reference, so every recursive call accumulates into the same result vector rather than building separate copies.
Preorder — Java
class Solution {
public void preOrder(TreeNode root, List<Integer> ans) {
if (root == null)
return;
ans.add(root.val); // process root first
preOrder(root.left, ans);
preOrder(root.right, ans);
}
}
The shape of the logic is identical:
Process Root → Traverse Left → Traverse Right
Inorder Traversal
Order: Left → Root → Right
Only one thing changes from preorder — when the root is processed:
Traverse the left subtree.
Visit the current node.
Traverse the right subtree.
Using the same tree, the result is:
4 → 2 → 5 → 1 → 3
Compare the two so far:
Preorder: 1 → 2 → 4 → 5 → 3
Inorder: 4 → 2 → 5 → 1 → 3
The tree hasn't changed. The recursive calls haven't changed. Only the position of the root's processing line moved.
Inorder — C++
class Solution {
public:
void inOrder(TreeNode* root, vector<int>& ans) {
if (root == nullptr)
return;
inOrder(root->left, ans);
ans.push_back(root->val); // process root in the middle
inOrder(root->right, ans);
}
};
Compared to preorder, ans.push_back(root->val) simply moved between the two recursive calls.
Inorder — Java
class Solution {
public void inOrder(TreeNode root, List<Integer> ans) {
if (root == null)
return;
inOrder(root.left, ans);
ans.add(root.val); // process root in the middle
inOrder(root.right, ans);
}
}
Traverse Left → Process Root → Traverse Right
Postorder Traversal
Order: Left → Right → Root
The root is processed only after both subtrees are fully traversed:
Traverse the left subtree.
Traverse the right subtree.
Visit the current node.
Using the same tree, the result is:
4 → 5 → 2 → 3 → 1
Again — same tree, same recursive calls, only the root's processing line moved, this time to the very end.
Postorder — C++
class Solution {
public:
void postOrder(TreeNode* root, vector<int>& ans) {
if (root == nullptr)
return;
postOrder(root->left, ans);
postOrder(root->right, ans);
ans.push_back(root->val); // process root last
}
};
Postorder — Java
class Solution {
public void postOrder(TreeNode root, List<Integer> ans) {
if (root == null)
return;
postOrder(root.left, ans);
postOrder(root.right, ans);
ans.add(root.val); // process root last
}
}
Traverse Left → Traverse Right → Process Root
Side-by-Side Comparison
| Traversal | Order | Root Position | Result (example tree) |
|---|---|---|---|
| Preorder | Root → Left → Right | First | 1 → 2 → 4 → 5 → 3 |
| Inorder | Left → Root → Right | Middle | 4 → 2 → 5 → 1 → 3 |
| Postorder | Left → Right → Root | Last | 4 → 5 → 2 → 3 → 1 |
Time & Space Complexity
All three traversals share the same complexity profile, since each visits every node exactly once:
Time complexity:
O(n), wherenis the number of nodes.Space complexity:
O(h)for the recursion call stack, wherehis the height of the tree.Balanced tree →
O(log n).Skewed (degenerate) tree →
O(n), since the call stack depth equals the number of nodes.
Note that the output list itself also takes O(n) space, independent of the traversal order.
How to Remember Them
You don't need to memorize three separate algorithms — just the meaning of the prefixes:
PREorder — pre = before → the root comes first:
Root → Left → RightINorder — in = in between → the root comes in the middle:
Left → Root → RightPOSTorder — post = after → the root comes last:
Left → Right → Root
Combine that with the one constant rule — left always comes before right — and the entire family of traversals collapses into a single idea:
PRE → ROOT first
IN → ROOT in the middle
POST → ROOT last





