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 the primary purpose of Binary Trees?
4. 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
What is Binary Trees?
Click to reveal answer
Answer
Binary Trees is a key concept in software engineering.
Question
When to use Binary Trees?
Click to reveal answer
Answer
Use Binary Trees when building production systems that require reliability, scalability, and maintainability.
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