Trees
Why Trees Matter
Trees enable efficient hierarchical data organization with O(log n) operations:
- Database indexes: B+ Trees power MySQL/PostgreSQL indexes (1000x faster lookups)
- File systems: Directory structures are trees
- DOM/JSON: HTML/XML documents, JSON objects
- Autocomplete: Trie-based search suggestions
- Routing: IP routing tables use tree structures (tries, prefix trees)
Real-world impact: A balanced BST with 1 billion nodes requires only 30 comparisons (log₂1,000,000,000) to find any element—versus 500 million comparisons on average for linear search. This 10 millionx speedup is why every database index uses trees.
Core Concepts
Binary Tree Structure
class TreeNode {
int val;
TreeNode left;
TreeNode right;
TreeNode(int val) { this.val = val; }
}
Key terminology:
- Root: Top node (no parent)
- Leaf: Node with no children
- Internal node: Node with at least one child
- Height: Longest path from node to leaf
- Depth: Distance from root
Binary Tree vs Binary Search Tree
| Property | Binary Tree | Binary Search Tree (BST) |
|---|---|---|
| Ordering | None | Left < Root < Right |
| Search | O(n) must check all | O(log n) use BST property |
| Insert | Anywhere | Maintain BST property |
| Use case | Heaps, expression trees | Dictionaries, sets |
BST property: For every node, all values in left subtree are smaller, all in right subtree are larger.
Balanced Trees
AVL Tree
Self-balancing BST where height difference between subtrees is at most 1:
class AVLNode {
int val, height;
AVLNode left, right;
AVLNode(int val) {
this.val = val;
this.height = 1;
}
}
Rotations to maintain balance:
- Left rotation: Right-heavy subtree
- Right rotation: Left-heavy subtree
- Left-Right rotation: Left child is right-heavy
- Right-Left rotation: Right child is left-heavy
Red-Black Tree
Balanced BST with color properties:
- Every node is red or black
- Root is black
- Red nodes cannot have red children (no double red)
- Every path from node to leaves has same number of black nodes
Java's TreeMap uses Red-Black trees:
- Guarantees O(log n) operations
- Fewer rotations than AVL (faster insert/delete)
- Slightly taller than AVL (still O(log n))
Deep Dive
Tree Traversals
Depth-First Traversals
Preorder (Root, Left, Right): 1, 2, 4, 5, 3, 6, 7
- Use case: Copy tree, prefix expression evaluation
Inorder (Left, Root, Right): 4, 2, 5, 1, 6, 3, 7
- Use case: BST yields sorted order
Postorder (Left, Right, Root): 4, 5, 2, 6, 7, 3, 1
- Use case: Delete tree (delete children first), postfix evaluation
Recursive Implementation
// Preorder traversal
public void preorder(TreeNode root) {
if (root == null) return;
System.out.print(root.val + " "); // Visit root
preorder(root.left); // Traverse left
preorder(root.right); // Traverse right
}
// Inorder traversal
public void inorder(TreeNode root) {
if (root == null) return;
inorder(root.left);
System.out.print(root.val + " ");
inorder(root.right);
}
// Postorder traversal
public void postorder(TreeNode root) {
if (root == null) return;
postorder(root.left);
postorder(root.right);
System.out.print(root.val + " ");
}
Iterative Implementation
// Preorder (iterative)
public List<Integer> preorderIterative(TreeNode root) {
List<Integer> result = new ArrayList<>();
if (root == null) return result;
Deque<TreeNode> stack = new ArrayDeque<>();
stack.push(root);
while (!stack.isEmpty()) {
TreeNode node = stack.pop();
result.add(node.val);
// Push right first (so left is processed first)
if (node.right != null) stack.push(node.right);
if (node.left != null) stack.push(node.left);
}
return result;
}
// Inorder (iterative)
public List<Integer> inorderIterative(TreeNode root) {
List<Integer> result = new ArrayList<>();
Deque<TreeNode> stack = new ArrayDeque<>();
TreeNode current = root;
while (current != null || !stack.isEmpty()) {
// Reach leftmost node
while (current != null) {
stack.push(current);
current = current.left;
}
current = stack.pop();
result.add(current.val);
current = current.right;
}
return result;
}
Breadth-First Traversal (Level Order)
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> result = new ArrayList<>();
if (root == null) return result;
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
while (!queue.isEmpty()) {
int levelSize = queue.size();
List<Integer> level = new ArrayList<>();
for (int i = 0; i < levelSize; i++) {
TreeNode node = queue.poll();
level.add(node.val);
if (node.left != null) queue.offer(node.left);
if (node.right != null) queue.offer(node.right);
}
result.add(level);
}
return result;
}