Trees

Medium
Time: O(n)Space: O(h) recursion/stack, O(w) BFS where h=height, w=max width
0/25
problems solved
0%
1.

What is a Tree?

A tree is a hierarchical data structure consisting of nodes connected by edges. Unlike linear structures (arrays, linked lists), trees represent hierarchical relationships.

The Family Tree Analogy: Think of a family tree:

  • The oldest ancestor is the root
  • Each person (node) can have children
  • Nodes with no children are leaves
  • Every node has exactly one parent (except root)

Visual Representation:

1 ← Root (level 0)
/ \
2 3 ← Level 1
/ \ \
4 5 6 ← Level 2 (leaves)

Why Trees Matter:

  • File systems (folders contain files/folders)
  • HTML/DOM (elements contain elements)
  • Organization charts
  • Decision trees
  • Expression parsing
  • Database indexes (B-trees)

Binary Trees: Each node has at most 2 children (left and right). Most interview questions focus on binary trees.

2.

The TreeNode Class

Before solving any tree problem, understand the node structure.

The Building Block:

class TreeNode {
int val; // The data
TreeNode left; // Left child (or null)
TreeNode right; // Right child (or null)
TreeNode(int val) {
this.val = val;
}
}

Visual Representation:

+-------+
| val |
+---+---+
/ \
left right
↓ ↓
node node
(or null)

Building a Tree Manually:

TreeNode root = new TreeNode(1);
root.left = new TreeNode(2);
root.right = new TreeNode(3);
root.left.left = new TreeNode(4);
// 1
// / \
// 2 3
// /
// 4

Key Insight: Unlike linked lists (one pointer), tree nodes have TWO pointers. This creates branching, which is why we need different traversal strategies.

3.

Tree Terminology

Master these terms - interviewers use them freely.

Node Relationships:

A ← Root (no parent)
/ \
B C ← B and C are siblings
/ \
D E ← D, E are children of B
← B is parent of D, E
← D, E are leaves (no children)

Measurements:

  • Depth of node: Distance from root (root depth = 0)
  • Height of node: Distance to deepest leaf below it
  • Height of tree: Height of root = max depth of any leaf
  • Level: All nodes at same depth

Example:

1 depth=0, height=2
/ \
2 3 depth=1, height=1
/ \
4 5 depth=2, height=0

Types of Binary Trees:

TypeDefinition
FullEvery node has 0 or 2 children
CompleteAll levels full except possibly last, filled left to right
PerfectAll internal nodes have 2 children, all leaves at same level
BalancedHeight is O(log n)
BSTLeft < Node < Right for all nodes
4.

Why Trees Matter in Interviews

Tree problems test multiple skills simultaneously.

What Interviewers Are Really Testing:

  1. Recursion Skills - Can you think recursively?
  2. Edge Case Handling - Null nodes, single node, skewed trees
  3. Choosing Right Traversal - Pre/In/Post/Level order
  4. State Management - Passing info down vs returning info up

Frequency in Interviews:

  • FAANG: 20-25% of coding rounds include tree problems
  • Most common: Max Depth, Validate BST, LCA, Level Order
  • Hardest: Serialize/Deserialize, Binary Tree Maximum Path Sum

The Two Fundamental Approaches:

DFS (Depth-First Search)
├── Uses recursion or stack
├── Goes deep before wide
└── Three orderings: Pre, In, Post
BFS (Breadth-First Search)
├── Uses queue
├── Goes wide before deep
└── Level-by-level processing

Golden Rule: Most tree problems reduce to "when do I process the node relative to its children?" Answer that, and you've chosen your traversal.

5.

DFS vs BFS - When to Use Which

The key decision in tree problems: depth-first or breadth-first?

DFS (Depth-First Search):

1
/ \
2 3
/ \
4 5
DFS visits: 1 → 2 → 4 → 5 → 3
(Goes all the way down before backtracking)

Use DFS when:

  • Computing values that bubble UP (height, subtree sums)
  • Path problems (root to leaf)
  • Tree structure problems (validate BST, same tree)
  • Most tree problems!

BFS (Breadth-First Search):

BFS visits: 1 → 2, 3 → 4, 5
(Level by level)

Use BFS when:

  • Level-order operations (level averages, zigzag)
  • Finding nodes at specific depth
  • Right/left side view
  • Shortest path in unweighted trees

Quick Decision:

Need level information? → BFS
Need to compute from leaves up? → DFS (postorder)
Need to pass info down? → DFS (preorder)
Need sorted BST values? → DFS (inorder)
6.

The Three DFS Traversal Orders

DFS has three orderings based on WHEN you process the current node.

The Key Question: When do I "visit" (process) the node?

1
/ \
2 3
/ \
4 5

Preorder (Node → Left → Right):

Visit order: 1, 2, 4, 5, 3
Process BEFORE going to children
Use for: Copying tree, serialization, path from root

Inorder (Left → Node → Right):

Visit order: 4, 2, 5, 1, 3
Process BETWEEN left and right children
Use for: BST gives SORTED order!

Postorder (Left → Right → Node):

Visit order: 4, 5, 2, 3, 1
Process AFTER both children
Use for: Computing from leaves up (height, deletion)

Memory Trick:

  • Pre = process pre-children (before)
  • In = process in-between children
  • Post = process post-children (after)
Code
Java
7.

Preorder Traversal Deep Dive

Preorder processes the node BEFORE its children. Think "top-down."

Visual Trace:

1
/ \
2 3
/ \
4 5
Call preorder(1):
Process 1 → output: [1]
Call preorder(2):
Process 2 → output: [1,2]
Call preorder(4):
Process 4 → output: [1,2,4]
preorder(null) returns
preorder(null) returns
Call preorder(5):
Process 5 → output: [1,2,4,5]
Call preorder(3):
Process 3 → output: [1,2,4,5,3]
Final: [1, 2, 4, 5, 3]

When to Use Preorder:

  • Serialization: Process node before children so you know structure
  • Copying a tree: Create node, then recursively copy children
  • Path from root: Build path as you go down
  • Printing tree structure: Parent printed before children
Code
Java
8.

Inorder Traversal Deep Dive

Inorder processes the node BETWEEN its children. For BST, this gives SORTED order!

Visual Trace:

4
/ \
2 6
/ \ \
1 3 7
Call inorder(4):
Call inorder(2):
Call inorder(1):
inorder(null) returns
Process 1 → output: [1]
inorder(null) returns
Process 2 → output: [1,2]
Call inorder(3):
Process 3 → output: [1,2,3]
Process 4 → output: [1,2,3,4]
Call inorder(6):
inorder(null) returns
Process 6 → output: [1,2,3,4,6]
Call inorder(7):
Process 7 → output: [1,2,3,4,6,7]
Final: [1, 2, 3, 4, 6, 7] ← SORTED!

Why BST + Inorder = Sorted: In BST: left < node < right Inorder visits: left, then node, then right → Visits nodes in ascending order!

When to Use Inorder:

  • Kth smallest in BST: Stop at kth node
  • Validate BST: Check if output is sorted
  • Convert BST to sorted array/list
Code
Java
9.

Postorder Traversal Deep Dive

Postorder processes the node AFTER its children. Think "bottom-up."

Visual Trace:

1
/ \
2 3
/ \
4 5
Call postorder(1):
Call postorder(2):
Call postorder(4):
postorder(null) returns
postorder(null) returns
Process 4 → output: [4]
Call postorder(5):
Process 5 → output: [4,5]
Process 2 → output: [4,5,2]
Call postorder(3):
Process 3 → output: [4,5,2,3]
Process 1 → output: [4,5,2,3,1]
Final: [4, 5, 2, 3, 1]

Why Postorder for "Bottom-Up": You need children's results BEFORE you can compute parent's result.

Example - Height Calculation:

height(node) = 1 + max(height(left), height(right))
You MUST know children's heights first!
→ That's postorder: children before parent.

When to Use Postorder:

  • Height/Depth: Need children's heights first
  • Diameter: Need both subtree heights
  • Delete tree: Delete children before parent
  • Subtree sums: Sum children before adding to parent
10.

Iterative DFS with Stack

Convert recursion to iteration using an explicit stack. Useful for deep trees to avoid stack overflow.

Key Insight: The call stack in recursion can be replaced with our own stack.

Iterative Preorder (Easiest):

Stack: [1]
Pop 1, process, push right(3) then left(2)
Stack: [3, 2]
Pop 2, process, push right(5) then left(4)
Stack: [3, 5, 4]
Pop 4, process
... continue
Note: Push right BEFORE left so left pops first!

Iterative Inorder: Trickier - need to go left first, then process, then go right.

Iterative Postorder: Trickiest - need to process AFTER both children. Use reverse trick or two stacks.

Code
Java
11.

BFS Level Order Traversal

BFS processes nodes level by level using a queue.

The Algorithm:

  1. Start with root in queue
  2. For each level:
    • Save queue size (nodes in this level)
    • Process exactly that many nodes
    • Add their children for next level

Visual Trace:

3
/ \
9 20
/ \
15 7
Initial queue: [3]
Level 0:
size = 1
Pop 3, add to level, enqueue 9, 20
Queue: [9, 20]
Output: [[3]]
Level 1:
size = 2
Pop 9, add to level
Pop 20, add to level, enqueue 15, 7
Queue: [15, 7]
Output: [[3], [9, 20]]
Level 2:
size = 2
Pop 15, Pop 7
Output: [[3], [9, 20], [15, 7]]

Why Save Size? The queue contains nodes from multiple levels. By saving size at loop start, we know exactly how many belong to current level.

Code
Java
12.

Maximum Depth of Binary Tree

A classic postorder problem: you need children's depths before computing parent's depth.

The Key Insight:

depth(node) = 1 + max(depth(left), depth(right))
depth(null) = 0

You MUST know children's depths first → Postorder!

Visual Trace:

3
/ \
9 20
/ \
15 7
depth(15) = 1 + max(0, 0) = 1
depth(7) = 1 + max(0, 0) = 1
depth(20) = 1 + max(1, 1) = 2
depth(9) = 1 + max(0, 0) = 1
depth(3) = 1 + max(1, 2) = 3
Max depth = 3

Alternative: BFS Count number of levels. Each level = 1 depth.

Code
Java
13.

Diameter of Binary Tree

The diameter is the longest path between ANY two nodes. Critical insight: the path may NOT pass through root!

The Key Insight: For each node, the longest path THROUGH that node = leftHeight + rightHeight.

We need to check this for EVERY node and track the maximum.

Example:

1
/ \
2 3
/ \
4 5
/
6
At node 2: left=2, right=1, path=3
At node 1: left=3, right=1, path=4
At node 4: left=1, right=0, path=1
Diameter = 4 (path: 6-4-2-1-3)

The Pattern: Postorder + Global Variable Compute heights (postorder), but track maximum path at each node using a closure/class variable.

Code
Java
14.

Binary Search Tree Properties

A Binary Search Tree (BST) maintains a special ordering property.

The BST Property: For every node:

  • ALL nodes in left subtree < node
  • ALL nodes in right subtree > node

Valid BST:

8
/ \
3 10
/ \ \
1 6 14
/ \
4 7
All nodes left of 8 are < 8 ✓
All nodes right of 8 are > 8 ✓
(Recursively true for all nodes)

Invalid BST:

5
/ \
1 4
/ \
3 6 ← 6 > 5, invalid!
6 is in right subtree of 4 ✓
But 6 is also in right subtree of 5 ✗
BST property must hold for ALL ancestors!

BST Operations:

OperationAverageWorst (Skewed)
SearchO(log n)O(n)
InsertO(log n)O(n)
DeleteO(log n)O(n)

Key BST Insight: Inorder traversal gives SORTED order!

15.

Validate Binary Search Tree

Checking if a tree is a valid BST is trickier than it looks.

Common Mistake - Only Check Children:

// WRONG!
boolean isValid(TreeNode node) {
if (node.left != null && node.left.val >= node.val) return false;
if (node.right != null && node.right.val <= node.val) return false;
return isValid(node.left) && isValid(node.right);
}

This misses cases where a node violates an ancestor's constraint!

Correct Approach - Pass Bounds Down: Each node must be within a valid range:

  • Left child: (min, parent)
  • Right child: (parent, max)

Visual:

10 (range: -∞ to +∞)
/ \
5 15 (range: 10 to +∞)
/ \
6 20
6 < 10! Invalid!
(range should be 10 to 15)
Code
Java
16.

Lowest Common Ancestor (LCA)

Find the deepest node that is an ancestor of both nodes p and q.

Visual:

3
/ \
5 1
/ \ / \
6 2 0 8
/ \
7 4
LCA(5, 1) = 3 (both are children of 3)
LCA(5, 4) = 5 (4 is descendant of 5)
LCA(6, 4) = 5 (both in left subtree of 3)

Algorithm for Binary Tree: Postorder search - look for p and q in both subtrees:

  1. If current is null, p, or q → return current
  2. Search left subtree
  3. Search right subtree
  4. If both sides found something → current is LCA
  5. Otherwise return whichever side found something

Algorithm for BST (Faster): Use BST property:

  • If both p and q < node → LCA is in left subtree
  • If both p and q > node → LCA is in right subtree
  • Otherwise → current node is LCA (split point)
Code
Java
17.

Path Sum Problems

Path problems come in several varieties. Know the differences!

Type 1: Root-to-Leaf Path Sum "Does any root-to-leaf path sum to target?"

5
/ \
4 8
/ / \
11 13 4
/ \ \
7 2 1
Target = 22
Path: 5→4→11→2 = 22 ✓

Type 2: Any Path (Can Start/End Anywhere) "Find max sum of any path" - much harder! Path can start and end at any node.

Type 3: Count Paths with Sum "How many paths sum to target?" (Path Sum III) Paths can start anywhere but must go downward.

Code
Java
18.

Serialize and Deserialize Binary Tree

Convert a tree to a string and back. A hard problem that tests deep understanding.

The Challenge: A tree structure must be fully recoverable from the string.

Approach: Preorder + Null Markers

1
/ \
2 3
/ \
4 5
Serialize (preorder with nulls):
"1,2,null,null,3,4,null,null,5,null,null"
Deserialize:
1. Read "1" → create root
2. Read "2" → create left child
3. Read "null" → 2's left is null
4. Read "null" → 2's right is null
5. Read "3" → create right child of 1
... and so on

Why Preorder Works: Preorder visits root first, so we know the structure from top-down. Combined with null markers, we know exactly when a subtree ends.

Code
Java
19.

Construct Tree from Traversals

Rebuild a tree given two traversal arrays. Classic interview problem!

What You Need:

  • Preorder + Inorder → Unique tree
  • Postorder + Inorder → Unique tree
  • Preorder + Postorder → NOT unique (unless full binary tree)

Why Inorder is Special: Inorder tells you which nodes are in left vs right subtree.

Algorithm (Preorder + Inorder):

Preorder: [3, 9, 20, 15, 7] ← First element is root
Inorder: [9, 3, 15, 20, 7] ← Splits left/right
1. Preorder[0] = 3 is root
2. Find 3 in Inorder → index 1
3. Left of index 1 in Inorder: [9] → left subtree
4. Right of index 1 in Inorder: [15, 20, 7] → right subtree
5. Recurse!
Code
Java
20.

Binary Tree Right Side View

Return values visible from the right side - a BFS classic!

Visual:

1 ← See 1
/ \
2 3 ← See 3 (rightmost)
\ \
5 4 ← See 4 (rightmost)
Output: [1, 3, 4]

Approach 1: BFS Process level by level, take last node of each level.

Approach 2: DFS Modified preorder: Root → Right → Left Track depth, add node if visiting new depth for first time.

Code
Java
21.

Common Edge Cases

Master these edge cases to avoid interview pitfalls.

1. Empty Tree (root = null)

if (root == null) return baseCase;

2. Single Node

if (root.left == null && root.right == null) {
// Leaf node - often a base case
}

3. Skewed Tree (Like a Linked List)

1 1
/ \
2 2
/ \
3 3
Height = n (worst case for BST operations)

4. Complete vs Incomplete

Complete: Incomplete:
1 1
/ \ / \
2 3 2 3
/ \ /
4 5 4

5. Null Children

// Always check before accessing children
if (node.left != null) {
// Safe to use node.left
}

6. Integer Overflow in BST Validation

// Use long, not int, for bounds
validate(root, Long.MIN_VALUE, Long.MAX_VALUE);

7. Duplicate Values Some BST problems allow duplicates. Clarify with interviewer!

22.

Pattern Recognition Guide

Quick reference for choosing the right approach:

Problem TypeApproachWhy
Max DepthPostorderNeed children's depths first
DiameterPostorder + globalCompute heights, track max
Validate BSTPreorder + boundsPass constraints down
Kth Smallest BSTInorderGives sorted order
Level OrderBFSProcess level by level
Right Side ViewBFS or DFS (right first)Need rightmost per level
SerializePreorder + nullsReconstruct top-down
LCAPostorderSearch both subtrees
Path Sum (root-leaf)PreorderBuild path going down
Same TreeAny DFSCompare node by node
Invert TreeAny DFSSwap children at each node

Decision Tree:

Need level information?
YES → BFS
Need to compute from leaves up?
YES → Postorder (height, diameter, subtree sums)
Need to pass info down?
YES → Preorder (validation, path building)
Need BST sorted order?
YES → Inorder
Need to compare two trees?
→ DFS, compare node by node
23.

Template Summary

Copy-paste ready templates for common tree operations.

Template 1: DFS Recursive

int dfs(TreeNode node) {
if (node == null) return BASE_CASE;
int left = dfs(node.left);
int right = dfs(node.right);
return COMBINE(left, right, node.val);
}

Template 2: BFS Level Order

Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
while (!queue.isEmpty()) {
int size = queue.size();
for (int i = 0; i < size; i++) {
TreeNode node = queue.poll();
// Process node
if (node.left != null) queue.offer(node.left);
if (node.right != null) queue.offer(node.right);
}
}

Template 3: BST Validation

boolean validate(TreeNode node, long min, long max) {
if (node == null) return true;
if (node.val <= min || node.val >= max) return false;
return validate(node.left, min, node.val) &&
validate(node.right, node.val, max);
}

Template 4: LCA

TreeNode lca(TreeNode root, TreeNode p, TreeNode q) {
if (root == null || root == p || root == q) return root;
TreeNode left = lca(root.left, p, q);
TreeNode right = lca(root.right, p, q);
if (left != null && right != null) return root;
return left != null ? left : right;
}

Template 5: Postorder with Global Max

int globalMax = 0;
int helper(TreeNode node) {
if (node == null) return 0;
int left = helper(node.left);
int right = helper(node.right);
globalMax = Math.max(globalMax, left + right);
return 1 + Math.max(left, right);
}

Test Your Knowledge

Take a quick 15question quiz to test what you've learned.