One recursive pattern, three visit timings. Change when you call result.add(node.val) — before, between, or after the recursive calls — to get pre-, in-, or post-order traversal.
Pre-order — serialize tree, clone, construct from traversal, prefix expression
In-order — produce sorted output from BST, validate BST, kth smallest
Post-order — compute height/diameter, delete tree, evaluate expression trees
All three: whenever you need to process every node exactly once
Bottom-up DFS return values — height, max path sum, diameter
💡 The only difference between the three is the placement of result.add(node.val): before both recursive calls (pre), between them (in), or after both (post).
⚡ Complexity
Time
O(n)
Space (stack)
O(h)
Where n = number of nodes, h = height of tree.
Best case (balanced): O(log n) stack space.
Worst case (skewed/linked list): O(n) stack space.
Every node is visited exactly once — O(n) time regardless of order.
Live Animation · DFS Tree Traversal · LC #144
Problem
Given a binary tree with 7 nodes (root = 1, left = [2,4,5], right = [3,6,7]), traverse it in Pre-order, In-order, and Post-order. Same tree — three visit orderings, each used for a different purpose: serialize, sort (BST), and compute height/diameter.
Input: root = [1,2,3,4,5,6,7] · Answer: Pre [1,2,4,5,3,6,7] · In [4,2,5,1,6,3,7] · Post [4,5,2,6,7,3,1]
Step 0 / 8
🔍 Init📌 Visit Node⬇️ Descend✅ Done
🌿 Pre-order: ROOT → Left → Right. Visit the current node before recursing into children. The root is always first, leaves appear in left-to-right order.
// dfs(node) — pre-order skeleton
🌲 Binary Tree
active
in stack
visited
default
📚 Call Stack
— empty —
📝 Result:
[ waiting... ]
📊 Traversal Comparison — Same Tree
🌿Pre-order
① 1② 2③ 4④ 5⑤ 3⑥ 6⑦ 7
Root visited FIRST
🔶In-order
① 4② 2③ 5④ 1⑤ 6⑥ 3⑦ 7
BST → sorted order
🟣Post-order
① 4② 5③ 2④ 6⑤ 7⑥ 3⑦ 1
Root visited LAST
🧩 Pattern Skeleton
Java 8
// ─── Pre-order (Root → Left → Right) ────────────────voidpreorder(TreeNode node, List<Integer> result) {
if (node == null) return;
result.add(node.val); // ① visit ROOT firstpreorder(node.left, result); // ② then left subtreepreorder(node.right, result); // ③ then right subtree
}
// ─── In-order (Left → Root → Right) ─────────────────voidinorder(TreeNode node, List<Integer> result) {
if (node == null) return;
inorder(node.left, result); // ① recurse left first
result.add(node.val); // ② visit ROOT betweeninorder(node.right, result); // ③ then right subtree
}
// ─── Post-order (Left → Right → Root) ───────────────voidpostorder(TreeNode node, List<Integer> result) {
if (node == null) return;
postorder(node.left, result); // ① recurse left firstpostorder(node.right, result); // ② then right subtree
result.add(node.val); // ③ visit ROOT last
}
// ─── Generic DFS (bottom-up, returns computed value) ─intdfs(TreeNode node) {
if (node == null) return0; // base caseint left = dfs(node.left); // get left resultint right = dfs(node.right); // get right result// use left + right to compute answer
ans = Math.max(ans, left + right); // e.g. diameterreturn1 + Math.max(left, right); // e.g. height
}