Overview
A binary tree is a hierarchy of nodes where each node has at most two children, left and right. The top node is the root, and nodes with no children are leaves. Trees represent hierarchical data and are the basis for search trees, heaps, tries, and expression trees.
Almost every binary tree problem is solved with recursion: define what the function returns for one node, handle the null case, and combine the answers from the left and right subtrees. The skill is choosing what information flows up (return values) and what flows down (parameters).
Each person can have up to two children listed below them. To count everyone in the family, you ask each child to count their own branch and add yourself: count(node) = 1 + count(left) + count(right).
When to use it
- Data is hierarchical: folders, org charts, HTML DOM, expression parsing.
- The problem gives a TreeNode root.
- You need to reason about paths, depths, subtrees, or ancestors.
- The answer for a node depends on the answers for its children.
Problem patterns it solves
Recognize it when: height, diameter, balanced check, max path sum: the answer combines child results.
- 104. Maximum Depth of Binary Tree
- 543. Diameter of Binary Tree
- 110. Balanced Binary Tree
- 124. Binary Tree Maximum Path Sum
Recognize it when: path sums, bounds, depth-so-far, good nodes.
- 112. Path Sum
- 1448. Count Good Nodes in Binary Tree
- 129. Sum Root to Leaf Numbers
Recognize it when: same tree, symmetric, subtree, merge, invert.
- 100. Same Tree
- 101. Symmetric Tree
- 572. Subtree of Another Tree
- 226. Invert Binary Tree
Recognize it when: deepest node that is an ancestor of both targets.
- 236. Lowest Common Ancestor of a Binary Tree
- 1650. LCA of a Binary Tree III
Recognize it when: construct from traversals, encode to a string and back.
- 105. Construct Binary Tree from Preorder and Inorder
- 297. Serialize and Deserialize Binary Tree
Recognize it when: all root-to-leaf paths or paths with a sum.
- 257. Binary Tree Paths
- 113. Path Sum II
- 437. Path Sum III
Where it is used in real software
Browsers parse HTML into a tree; React and other frameworks diff component trees to decide what to re-render.
Source code becomes an abstract syntax tree; expression trees hold operators as internal nodes and values as leaves.
Directories and files form a tree; disk usage tools compute folder sizes with a bottom-up traversal.
Models such as random forests and gradient-boosted trees classify data by walking yes/no questions from the root to a leaf.
Key terms
- Root / leaf
- The top node / a node with no children.
- Height
- Number of nodes (or edges) on the longest root-to-leaf path.
- Depth
- Distance from the root to a node.
- Full / complete / perfect
- Every node has 0 or 2 children / all levels full except the last, filled left to right / all levels completely full.
- Subtree
- A node together with all its descendants.
Solving tree problems recursively
- 1Define the return value
Example: height(node) returns the height of the subtree rooted at node.
- 2Handle null
height(null) = 0. The null case is the base case for almost every tree function.
- 3Ask the children
left = height(node.left), right = height(node.right). Trust the recursion.
- 4Combine
return 1 + max(left, right).
- 5Use a global when the answer is not the return value
Diameter returns height but updates a best variable with left + right at each node.
Diameter of a binary tree
Tree: 1 has children 2 and 3; 2 has children 4 and 5. Diameter = longest path in edges.
| Node | Left height | Right height | Path through node | Returns height | Best diameter |
|---|---|---|---|---|---|
| 4 | 0 | 0 | 0 | 1 | 0 |
| 5 | 0 | 0 | 0 | 1 | 0 |
| 2 | 1 | 1 | 2 | 2 | 2 |
| 3 | 0 | 0 | 0 | 1 | 2 |
| 1 | 2 | 1 | 3 | 3 | 3 |
NOWNode: 4 | Left height: 0 | Right height: 0 | Path through node: 0 | Returns height: 1 | Best diameter: 0
The diameter is 3 (path 4 - 2 - 1 - 3). The function returns height to its parent but records left + right as a candidate answer, a pattern you will reuse for maximum path sum.
Implementation
class TreeNode { constructor(val, left = null, right = null) { this.val = val; this.left = left; this.right = right; }} const maxDepth = (node) => (node ? 1 + Math.max(maxDepth(node.left), maxDepth(node.right)) : 0); function diameterOfBinaryTree(root) { let best = 0; function height(node) { if (!node) return 0; const left = height(node.left); const right = height(node.right); best = Math.max(best, left + right); // path passing through node return 1 + Math.max(left, right); } height(root); return best;} function isBalanced(root) { // returns -1 when unbalanced so we can stop early function check(node) { if (!node) return 0; const left = check(node.left); if (left === -1) return -1; const right = check(node.right); if (right === -1 || Math.abs(left - right) > 1) return -1; return 1 + Math.max(left, right); } return check(root) !== -1;} function lowestCommonAncestor(root, p, q) { if (!root || root === p || root === q) return root; const left = lowestCommonAncestor(root.left, p, q); const right = lowestCommonAncestor(root.right, p, q); if (left && right) return root; // p and q are on different sides return left ?? right;}Complexity and performance
Each node is processed once.
h = height; O(log n) balanced, O(n) skewed.
Complete tree height = floor(log2 n).
Trade-offs
Recursive code is shorter and mirrors the definition. Iterative code with an explicit stack avoids stack overflow on very deep trees.
A global best is concise; returning [height, best] keeps the function pure.
Variants and related techniques
Replace left/right with a children array; the same recursion applies.
With parent links, LCA becomes an intersection problem similar to linked lists.
Complete trees (heaps) store children of i at 2i + 1 and 2i + 2.
Common mistakes
- Missing the null base case.
Fix: Start every recursive tree function with if (!node) return base.
- Confusing height in nodes and in edges.
Fix: State which one you use; diameter in edges is left + right when heights count nodes.
- Returning the wrong thing upward in path problems.
Fix: A path can bend at a node but only one branch can continue to the parent.
Interview questions
How do you choose between top-down and bottom-up?
If a node needs information from its ancestors (running sum, bounds), pass it down. If it needs information from its descendants (height, subtree sum), return it up.
What is the space complexity of recursive tree traversal?
O(h), the height of the tree, because only one root-to-node path is on the call stack at a time.
Practice problems
| Problem | Difficulty | What it trains |
|---|---|---|
| 104. Maximum Depth of Binary Tree | Easy | Bottom-up height. |
| 226. Invert Binary Tree | Easy | Transform recursively. |
| 543. Diameter of Binary Tree | Easy | Global best plus returned height. |
| 236. Lowest Common Ancestor of a Binary Tree | Medium | Signals from both sides. |
| 105. Construct Binary Tree from Preorder and Inorder | Medium | Split by root index. |
| 124. Binary Tree Maximum Path Sum | Hard | Bend vs extend. |