Tree Pattern Templates
Scope — An index from every tree pattern to the one card that teaches it, plus the traversal forms no core sheet spells out: iterative pre- and post-order, zigzag levels, the bottom-left value and vertical order. See also: binary_tree.md — the binary-tree templates, one card each; tree.md — concepts, tree types, when to use which traversal; tree_lca_distance.md — LCA, node distance and root-to-leaf paths; tree_examples.md — the worked problems; tree_construction.md and tree_codec.md — building a tree from an encoding, and serialising it back; bst.md — ordered trees.
LeetCode Problem Lists
Overview
This sheet used to hold its own copy of every tree template, next to the copies in binary_tree.md, tree_lca_distance.md and tree_examples.md, and the copies drifted apart. Each pattern now has one home, and every section below says where it is. What stays here is the handful of forms that have no other home: the explicit-stack pre- and post-order walks, the zigzag BFS, the bottom-left value, and the sort-by-coordinate vertical order.
Recognition
Only the forms this sheet still owns; every other tree signal is in binary_tree.md’s table.
| Signal in the statement | Constraint cue | Reach for | Not this if |
|---|---|---|---|
| “preorder traversal”, and the follow-up “iteratively” | n ≤ 100 | 1.1) one stack, right child pushed first | in-order without recursion → binary_tree § Template 2 |
| “postorder traversal”, “iteratively” | n ≤ 100 | 1.3) two stacks, or a reversed root-right-left walk | the order does not matter, only the visit → any traversal |
| “zigzag level order”, “alternate left-to-right and right-to-left” | n ≤ 2000 | 1.5) level BFS, flip every other level | every level in one direction → binary_tree § Template 2 |
| “leftmost value in the last row” | n ≤ 10⁴ | 2.7) index 0 of every level | the rightmost value of every level (LC 199) → tree_examples.md |
| “vertical order”, ties broken by value | n ≤ 1000 | 8.3) collect (col, row, val), sort |
ties kept in BFS order (LC 314) → BFS grouping by column, no sort |
1) Tree Traversal Templates
| What this section used to hold | Where it lives now |
|---|---|
| 1.2) Inorder Template — LC 94 | binary_tree § Template 1 (recursive) and § Template 2 (the stack form); the O(1)-space Morris walk is tree.md § Template 1 |
| 1.4) BFS Template (Level-order) — LC 102 | binary_tree § Template 2 — the queue-length snapshot |
1.1) Preorder Template — LC 144
Pattern: Root → Left → Right Use Case: When you need parent data before processing children Time Complexity: O(n) Space Complexity: O(h) for recursion stack
Template Code
# Python - Recursive
def preorder_traversal(root):
result = []
def preorder(node):
if not node:
return
# Process root first
result.append(node.val)
# Then left subtree
preorder(node.left)
# Then right subtree
preorder(node.right)
preorder(root)
return result
# Python - Iterative
def preorder_iterative(root):
if not root:
return []
result = []
stack = [root]
while stack:
node = stack.pop()
result.append(node.val)
# Add right first (LIFO - will process left first)
if node.right:
stack.append(node.right)
if node.left:
stack.append(node.left)
return result
// Java - Recursive
public void preorderTraversal(TreeNode root, List<Integer> result) {
if (root == null) return;
result.add(root.val); // Process root
preorderTraversal(root.left, result); // Left subtree
preorderTraversal(root.right, result); // Right subtree
}
// Java - Iterative
public List<Integer> preorderIterative(TreeNode root) {
List<Integer> result = new ArrayList<>();
if (root == null) return result;
Stack<TreeNode> stack = new Stack<>();
stack.push(root);
while (!stack.isEmpty()) {
TreeNode node = stack.pop();
result.add(node.val);
if (node.right != null) stack.push(node.right);
if (node.left != null) stack.push(node.left);
}
return result;
}
LeetCode Problems
- LC 144: Binary Tree Preorder Traversal (Easy)
- LC 589: N-ary Tree Preorder Traversal (Easy)
1.3) Postorder Template — LC 145
Pattern: Left → Right → Root Use Case: Need children data before parent processing Time Complexity: O(n) Space Complexity: O(h)
Template Code
# Python - Recursive
def postorder_traversal(root):
result = []
def postorder(node):
if not node:
return
# Left subtree first
postorder(node.left)
# Right subtree
postorder(node.right)
# Process root last
result.append(node.val)
postorder(root)
return result
# Python - Iterative (Two Stacks)
def postorder_iterative(root):
if not root:
return []
stack1 = [root]
stack2 = []
# Collect nodes in reverse postorder
while stack1:
node = stack1.pop()
stack2.append(node)
if node.left:
stack1.append(node.left)
if node.right:
stack1.append(node.right)
# Pop from stack2 to get postorder
result = []
while stack2:
result.append(stack2.pop().val)
return result
// Java - Recursive
public void postorderTraversal(TreeNode root, List<Integer> result) {
if (root == null) return;
postorderTraversal(root.left, result); // Left subtree
postorderTraversal(root.right, result); // Right subtree
result.add(root.val); // Current node
}
LeetCode Problems
- LC 145: Binary Tree Postorder Traversal (Easy)
- LC 590: N-ary Tree Postorder Traversal (Easy)
1.5) BFS + Direction Template — LC 103
Pattern: Alternating direction per level Use Case: Zigzag traversal Time Complexity: O(n) Space Complexity: O(w)
Template Code
# Python - Zigzag Level Order
from collections import deque
def zigzag_level_order(root):
if not root:
return []
result = []
queue = deque([root])
left_to_right = True
while queue:
level_size = len(queue)
current_level = deque()
for _ in range(level_size):
node = queue.popleft()
# Add to level based on direction
if left_to_right:
current_level.append(node.val)
else:
current_level.appendleft(node.val)
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
result.append(list(current_level))
left_to_right = not left_to_right
return result
// Java - Zigzag Level Order
public List<List<Integer>> zigzagLevelOrder(TreeNode root) {
List<List<Integer>> result = new ArrayList<>();
if (root == null) return result;
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
boolean leftToRight = true;
while (!queue.isEmpty()) {
int levelSize = queue.size();
LinkedList<Integer> currentLevel = new LinkedList<>();
for (int i = 0; i < levelSize; i++) {
TreeNode node = queue.poll();
if (leftToRight) {
currentLevel.addLast(node.val);
} else {
currentLevel.addFirst(node.val);
}
if (node.left != null) queue.offer(node.left);
if (node.right != null) queue.offer(node.right);
}
result.add(currentLevel);
leftToRight = !leftToRight;
}
return result;
}
LeetCode Problems
- LC 103: Binary Tree Zigzag Level Order Traversal (Medium)
2) Tree Property Templates
| What this section used to hold | Where it lives now |
|---|---|
| 2.1) Postorder Height Template — LC 104 | Maximum Depth of Binary Tree — LC 104 |
| 2.2) BFS Early Stop Template — LC 111 | Minimum Depth of Binary Tree — LC 111 — the first leaf a BFS pops |
| 2.3) Height Validation Template — LC 110 | Balanced Binary Tree — LC 110 — return -1 as the unbalanced signal |
| 2.4) Mirror Validation Template — LC 101 | Symmetric Tree — LC 101 |
| 2.5) Tree Comparison Template — LC 100 | binary_tree § Template 12 — two trees in lockstep |
| 2.6) Minimum Depth — the recursive form and its single-child trap | the same LC 111 card, whose traps cover the one-child node |
2.7) Leftmost Value at Maximum Depth — LC 513
Key point — a level-order BFS meets the levels top to bottom and each level left to right, so the first node popped on each level is that level’s leftmost; the last value recorded that way is the bottom-left one.
Reduces to: binary_tree § Template 2 — the queue-length snapshot, recording index 0 of every level instead of the whole level.
# python
# LC 513 - Find Bottom Left Tree Value
# IDEA: level-order BFS; the first node of each level overwrites the answer, so the last level wins
# time = O(n), space = O(w) w = maximum width of a level
from collections import deque
def find_bottom_left_value(root):
queue = deque([root])
leftmost = root.val
while queue:
level_size = len(queue)
for i in range(level_size):
node = queue.popleft()
if i == 0: # first node of this level
leftmost = node.val
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
return leftmost
// java
// LC 513 - Find Bottom Left Tree Value
// IDEA: level-order BFS; record index 0 of every level
// time = O(n), space = O(w)
public int findBottomLeftValue(TreeNode root) {
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
int leftmost = root.val;
while (!queue.isEmpty()) {
int levelSize = queue.size();
for (int i = 0; i < levelSize; i++) {
TreeNode node = queue.poll();
if (i == 0) {
leftmost = node.val;
}
if (node.left != null) {
queue.offer(node.left);
}
if (node.right != null) {
queue.offer(node.right);
}
}
}
return leftmost;
}
Traps
- “Leftmost” is per level, not “keep going left”: in
[1,2,3,4,null,5,6,null,null,7]the answer 7 sits under the right child. - Enqueue right before left and the last node popped is the bottom-left one: a one-variable version, but the order is the whole trick, so say it out loud.
3) Path-Based Templates
Every path template moved to the sheet that owns paths, or to the height card the bent-path problems reduce to:
| What this section used to hold | Where it lives now |
|---|---|
| 3.1) Global Max Update Template — LC 124 | Binary Tree Maximum Path Sum — LC 124 |
| 3.2) Path Accumulation Template — LC 112 | Path Sum — LC 112 |
| 3.3) Path + Backtrack Template — LC 113 | tree_lca_distance § Template 4 |
| 3.4) Path Count Tracking Template — LC 437 | tree_lca_distance § Template 5 |
| 3.5) Path Value Building Template — LC 129 | Sum Root to Leaf Numbers — LC 129 |
| 3.6) Path State Tracking Template — LC 1448 | Count Good Nodes in Binary Tree — LC 1448 |
| 3.7) Longest Path Template — LC 543 | binary_tree § Template 5 |
| 3.8) Same Value Path Template — LC 687 | Longest Univalue Path — LC 687 |
4) Distance and LCA Templates
These four templates moved out of this sheet. tree_lca_distance.md owns them and teaches each one at several times the length this sheet had room for:
| What this section used to hold | Where it lives now |
|---|---|
| 4.1) LCA Standard Template — LC 236 | LCA — LC 236, plus the LC 865 / 1123 deepest-nodes variant |
| 4.2) Value Comparison Template — LC 235 | the same section — LC 235 is the BST shortcut on the same template |
| 4.3) Path Distance Template — LC 1740 | Distance Between Nodes — LC 1740 |
| 4.4) Tree to Graph Template — LC 863 | Move Parent Pattern, which is the general form — LC 863 and LC 742 are both instances |
The idea worth carrying away from here: every “distance” question on a tree is an LCA
question in disguise, because the only path between two nodes runs through their lowest common
ancestor — dist(p, q) = depth(p) + depth(q) - 2·depth(lca). When you also need to walk
upward, add parent pointers and treat the tree as an undirected graph.
6) Tree Construction Templates
Construction moved out of this sheet as well; two Tier 1 sheets own it between them:
| What this section used to hold | Where it lives now |
|---|---|
| 6.1) Tree Building Template — LC 105 / 106 | tree_construction.md — this sheet’s Java template and its LC 106 post-order variant were merged in |
| 6.2) String Conversion Template — LC 297 | tree_codec.md — the whole codec family, LC 297 / 449 / 331 |
| 6.3) String Construction Template — LC 606 | tree_codec.md — the parenthesis format and the pair-omission rule |
The idea worth carrying away from here: every construction problem is the same recursion —
identify the root from the encoding, work out how much of the input belongs to each subtree, and
recurse. Only the first step differs: the pre-order head, the post-order tail, the maximum of a
range, or the token before the first (.
7) Tree Modification Templates
| What this section used to hold | Where it lives now |
|---|---|
| 7.1) Tree Inversion Template — LC 226 | Invert Binary Tree — LC 226 |
| 7.2) Tree Flattening Template — LC 114 | Flatten Binary Tree to Linked List — LC 114 |
| 7.3) Tree Merging Template — LC 617 | Merge Two Binary Trees — LC 617 |
| Deleting nodes and returning the forest — LC 1110 | binary_tree § Template 10 |
8) Advanced Tree Templates
| What this section used to hold | Where it lives now |
|---|---|
| 8.1) O(1)-Space Level Linking Template — LC 117 | binary_tree § Template 8 |
| 8.2) Postorder Tree DP (Pair Return) Template — LC 337 | binary_tree § Template 9 |
| 8.4) Complete Tree Node Count Template — LC 222 | binary_tree § Template 7 |
8.3) Coordinate Map Traversal Template — LC 987
Key point — give every node a coordinate (
left → col - 1,right → col + 1, depth →row) and the output order is a plain sort by(col, row, val). A BFS alone is not enough: two nodes can share(col, row), and the tie is broken by value, so collect first, then sort.
Reduces to: binary_tree § Template 1 —
any traversal that carries (row, col) down, followed by one sort.
# python
# LC 987 - Vertical Order Traversal of a Binary Tree
# IDEA: DFS collecting (col, row, val); plain tuple sort gives col → row → val
# time = O(N log N), space = O(N)
def vertical_traversal(root):
nodes = []
def dfs(node, row, col):
if not node:
return
nodes.append((col, row, node.val))
dfs(node.left, row + 1, col - 1)
dfs(node.right, row + 1, col + 1)
dfs(root, 0, 0)
nodes.sort()
res, prev_col = [], None
for col, row, val in nodes:
if col != prev_col:
res.append([])
prev_col = col
res[-1].append(val)
return res
// java
// LC 987 - Vertical Order Traversal of a Binary Tree
// IDEA: DFS collecting (col, row, val) triples, then sort col → row → val
// time = O(N log N), space = O(N)
public List<List<Integer>> verticalTraversal(TreeNode root) {
List<int[]> nodes = new ArrayList<>(); // {col, row, val}
dfs(root, 0, 0, nodes);
nodes.sort((a, b) -> a[0] != b[0] ? Integer.compare(a[0], b[0])
: a[1] != b[1] ? Integer.compare(a[1], b[1])
: Integer.compare(a[2], b[2]));
List<List<Integer>> res = new ArrayList<>();
Integer prevCol = null;
for (int[] n : nodes) {
if (prevCol == null || n[0] != prevCol) {
res.add(new ArrayList<>());
prevCol = n[0];
}
res.get(res.size() - 1).add(n[2]);
}
return res;
}
private void dfs(TreeNode node, int row, int col, List<int[]> nodes) {
if (node == null) return;
nodes.add(new int[]{col, row, node.val});
dfs(node.left, row + 1, col - 1, nodes);
dfs(node.right, row + 1, col + 1, nodes);
}
Traps
- Grouping by column with a BFS and no sort: LC 987 example 3 has 5 and 6 at the same
(col, row), and they must come out as[1,5,6], sorted by value, not in visiting order. - LC 314 (vertical order, no value tie-break) wants BFS order within a column, so there the sort
key is
(col, BFS index), not(col, row, val).
Drill Path
| Stage | Problems (easy → hard) | What it adds |
|---|---|---|
| Core | LC 144 no verdict · LC 145 no verdict · LC 103 no verdict | the two explicit-stack walks, then a level BFS whose direction flips |
| Practice | LC 513 no verdict · LC 589 no verdict · LC 590 no verdict | the last level’s first node; the same stack walks on an N-ary tree |
| Stretch | LC 987 no verdict · LC 314 no verdict | coordinates, with ties broken by value, then by BFS order |