Binary Tree Fundamentals
A binary tree is a hierarchical data structure where each node has at most two children, referred to as left and right.
Node Structure
class TreeNode {
int val;
TreeNode left;
TreeNode right;
TreeNode() {}
TreeNode(int val) { this.val = val; }
TreeNode(int val, TreeNode left, TreeNode right) {
this.val = val;
this.left = left;
this.right = right;
}
}
Key Terminology
| Term | Definition |
|---|---|
| Root | Topmost node (no parent) |
| Leaf | Node with no children |
| Height | Longest path from node to leaf |
| Depth | Distance from root to node |
| Degree | Number of children (0, 1, or 2) |
Tree Types
- Full Binary Tree: Every node has 0 or 2 children
- Complete Binary Tree: All levels filled except possibly last, filled left to right
- Balanced Binary Tree: Height difference between left and right subtrees ≤ 1
Visual Example
1 ← Root (depth=0, height=3)
/ \
2 3 ← depth=1
/ \\ \
4 5 6 ← depth=2
/ \\ \
7 8 9 ← Leaves (depth=3)
Building a Binary Tree
// Manual construction
TreeNode root = new TreeNode(1);
root.left = new TreeNode(2);
root.right = new TreeNode(3);
root.left.left = new TreeNode(4);
root.left.right = new TreeNode(5);
Computing Height Recursively
public int height(TreeNode root) {
if (root == null) return -1; // or 0 for node count
return 1 + Math.max(height(root.left), height(root.right));
}
Counting Nodes
public int countNodes(TreeNode root) {
if (root == null) return 0;
return 1 + countNodes(root.left) + countNodes(root.right);
}
Key Insight: Recursive Thinking
Every binary tree problem follows the same pattern:
- Base case:
nullnode → return default value - Recursive case: solve for left subtree, solve for right subtree
- Combine: use current node's value with left/right results
This "trust the recursion" approach is the foundation of all tree problems.
DFS vs BFS in Trees
Depth-First Search (DFS)
Explores as deep as possible before backtracking. Three variants:
// 1. Pre-order: Root → Left → Right
public void preorder(TreeNode root) {
if (root == null) return;
System.out.print(root.val + " "); // Visit root first
preorder(root.left);
preorder(root.right);
}
// 2. In-order: Left → Root → Right
public void inorder(TreeNode root) {
if (root == null) return;
inorder(root.left);
System.out.print(root.val + " "); // Visit root between
inorder(root.right);
}
// 3. Post-order: Left → Right → Root
public void postorder(TreeNode root) {
if (root == null) return;
postorder(root.left);
postorder(root.right);
System.out.print(root.val + " "); // Visit root last
}
Breadth-First Search (BFS)
Explores level by level using a queue:
public void levelOrder(TreeNode root) {
if (root == null) return;
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();
System.out.print(node.val + " ");
if (node.left != null) queue.offer(node.left);
if (node.right != null) queue.offer(node.right);
}
System.out.println(); // New level
}
}
Comparison
| Aspect | DFS | BFS |
|---|---|---|
| Data Structure | Stack (or recursion) | Queue |
| Space Complexity | O(h) height | O(w) max width |
| Use Case | Path finding, tree height | Level-order, shortest path |
| Iterative? | Yes (with explicit stack) | Yes (with queue) |
When to Use What
- DFS: Problems involving paths, subtrees, or exploring all possibilities
- BFS: Problems involving level-by-level processing, minimum depth, or shortest path
Practice Problems
Given the root of a binary tree, return its maximum depth. Maximum depth is the number of nodes along the longest path from the root node down to the farthest leaf node.
Example:
Input: root = [3,9,20,null,null,15,7]
Output: 3
Path 3→20→7 or 3→20→15 has 3 nodes.
Optimal Solution — O(n) time, O(h) where h is tree height, O(log n) for balanced, O(n) worst case space
Recursive DFS - max depth = 1 + max(left depth, right depth)
class Solution {
public int maxDepth(TreeNode root) {
if (root == null) return 0;
return 1 + Math.max(maxDepth(root.left), maxDepth(root.right));
}
} Edge Cases:
- Empty tree (root is null) → return 0
- Single node → return 1
- Skewed tree (all left or all right) → return n
Given two integer arrays preorder and inorder, construct and return the binary tree.
Example:
Input: preorder = [3,9,20,15,7], inorder = [9,3,15,20,7]
Output: [3,9,20,null,null,15,7]
Preorder gives root first. Inorder splits into left and right subtrees.
Optimal Solution — O(n) time, O(n) for recursion and map space
Recursive: first element of preorder is root, find root in inorder to split
class Solution {
public TreeNode buildTree(int[] preorder, int[] inorder) {
return build(preorder, 0, preorder.length - 1, inorder, 0, inorder.length - 1);
}
private TreeNode build(int[] preorder, int preStart, int preEnd,
int[] inorder, int inStart, int inEnd) {
if (preStart > preEnd || inStart > inEnd) return null;
int rootVal = preorder[preStart];
TreeNode root = new TreeNode(rootVal);
int rootIndex = inStart;
while (inorder[rootIndex] != rootVal) rootIndex++;
int leftSize = rootIndex - inStart;
root.left = build(preorder, preStart + 1, preStart + leftSize, inorder, inStart, rootIndex - 1);
root.right = build(preorder, preStart + leftSize + 1, preEnd, inorder, rootIndex + 1, inEnd);
return root;
}
} Edge Cases:
- Single node
- Only left children
- Only right children
Given the root of a binary tree, imagine yourself standing on the right side of it, return the values of the nodes you can see ordered from top to bottom.
Example:
Input: root = [1,2,3,null,5,null,4]
Output: [1,3,4]
From the right side, you see nodes 1, 3, and 4.
Optimal Solution — O(n) time, O(n) space
BFS: last node at each level is the right side view
class Solution {
public List<Integer> rightSideView(TreeNode root) {
List<Integer> result = new ArrayList<>();
if (root == null) return result;
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();
if (i == size - 1) result.add(node.val);
if (node.left != null) queue.offer(node.left);
if (node.right != null) queue.offer(node.right);
}
}
return result;
}
} Edge Cases:
- Empty tree
- Single node
- Left-skewed tree
Given the root of a binary tree, flatten the tree into a linked list in-place using preorder traversal.
Example:
Input: root = [1,2,5,3,4,null,6]
Output: [1,null,2,null,3,null,4,null,5,null,6]
Right child points to next node in preorder.
Optimal Solution — O(n) time, O(h) recursion depth space
Reverse preorder: process right, then left, maintain previous pointer
class Solution {
private TreeNode prev = null;
public void flatten(TreeNode root) {
if (root == null) return;
flatten(root.right);
flatten(root.left);
root.right = prev;
root.left = null;
prev = root;
}
} Edge Cases:
- Empty tree
- Single node
- Already flat tree
Quiz
1. What is the maximum number of nodes in a binary tree of height h?
2. Which traversal visits nodes in Left → Root → Right order?
3. What is a common mistake when implementing Binary Trees?
Flashcards
Question
What are the three DFS traversal orders for binary trees?
Click to reveal answer
Answer
Pre-order (Root→Left→Right), In-order (Left→Root→Right), Post-order (Left→Right→Root). Each visits root at a different point relative to children.
Question
What is the recursive pattern for binary tree problems?
Click to reveal answer
Answer
1) Base case: null node returns default. 2) Recursive case: solve left and right subtrees. 3) Combine: use current node's value with left/right results.
Question
Binary Trees best practices
Click to reveal answer
Answer
Follow SOLID principles, write clean code, test thoroughly, document decisions, and monitor in production.
Revision Notes
Key Takeaways
- 1. Every binary tree problem follows the recursive template: base case + recurse + combine
- 2. DFS uses stack (or recursion), BFS uses queue
- 3. In-order traversal of BST yields sorted order
- 4. Tree height determines DFS space complexity
Interview Tips
- • Start with recursive solution, then optimize to iterative if needed
- • Always handle null root as base case
- • Clarify if tree is balanced - affects complexity
- • Mention both time O(n) and space O(h) complexities
Cheat Sheet
Binary Trees Cheat Sheet
Node Definition:
class TreeNode {
int val;
TreeNode left, right;
}
Core Recursion Template:
public ReturnType solve(TreeNode root) {
if (root == null) return baseValue;
ReturnType left = solve(root.left);
ReturnType right = solve(root.right);
return combine(root.val, left, right);
}
DFS Traversals:
- Pre-order: Root → Left → Right (copy tree, serialize)
- In-order: Left → Root → Right (sorted order in BST)
- Post-order: Left → Right → Root (delete tree, compute height)
BFS Template:
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);
}
}
Key Formulas:
- Max nodes at level i: 2^i
- Max nodes in tree height h: 2^(h+1) - 1
- Height = ⌊log₂(n)⌋ for balanced tree