Recursion Practice Problems
How to Use This Problem List
Recursion problems fall into a small number of structural patterns. Recognising the pattern within two minutes of reading is the key skill — not the implementation itself.
The two most important questions for any recursion problem:
- ›What is the smallest version of this problem? (→ base case)
- ›How does solving a smaller version help solve the full problem? (→ recursive case)
If you can answer both clearly before writing code, the implementation follows mechanically.
Pattern 1: Linear Recursion — Reduce and Return
The simplest pattern. One recursive call per level, problem shrinks by a fixed amount (usually 1 or 1/2), result is computed by combining the current level's data with the sub-call's result.
| Problem | Difficulty | Recognition Clue | Key Insight |
|---|---|---|---|
| Factorial | Easy | "n! = n × (n-1)!" | f(n) = n × f(n-1), base f(0) = 1 |
| Sum of 1 to N | Easy | "sum first n integers recursively" | f(n) = n + f(n-1), base f(0) = 0 |
| Power of Two | Easy | "is n a power of 2 recursively" | Divide by 2 each call; base: n==1 true, n==0 false |
| Reverse a String | Easy | "reverse string without built-in" | f(s) = f(s[1:]) + s[0], base empty string |
| Count Occurrences in Array | Easy | "count target in array recursively" | f(arr, i) = (arr[i]==target) + f(arr, i+1) |
| Sum of Digits | Easy | "sum digits of integer recursively" | f(n) = (n%10) + f(n//10), base n < 10 |
| Check Palindrome | Easy | "is string a palindrome recursively" | Match outer chars, recurse on inner substring |
| GCD (Euclidean) | Easy | "greatest common divisor recursively" | gcd(a,b) = gcd(b, a%b), base b==0 return a |
| Count Zeros in Integer | Easy | "count zeros in digits of n" | f(n) = (n%10==0) + f(n//10), base n < 10 |
| First Index of Element | Easy | "first index where arr[i] == target" | Return i if match, else f(arr, target, i+1) |
Complexity target: O(n) time, O(n) space (call stack).
Recognition signal: "recursively compute X for n", "check property of n", "transform string/number one step at a time". A single chain of calls where each level does O(1) work.
Pattern 2: Divide and Conquer
Split the problem into two or more smaller subproblems of roughly equal size, solve each recursively, then merge the results. The recursion tree is a balanced binary tree.
| Problem | Difficulty | Recognition Clue | Key Insight |
|---|---|---|---|
| Binary Search | Easy | "find target in sorted array O(log n)" | Compare mid, recurse into one half only |
| Merge Sort | Medium | "sort array using divide and conquer" | Split in half, sort each, merge O(n) |
| Maximum Subarray (divide conquer) | Hard | "max subarray using divide and conquer" | Max is in left, right, or crosses mid |
| Count Inversions | Medium | "count pairs where i<j but arr[i]>arr[j]" | Merge sort variant: count right-picks during merge |
| Median of Array | Hard | "find median using divide and conquer" | QuickSelect: partition, recurse into one half |
| Closest Pair of Points | Hard | "minimum distance between any two points" | Split by x-coord, merge considers strip near divide line |
| Exponentiation by Squaring | Medium | "x^n in O(log n)" | pow(x,n) = pow(x,n/2)² if n even, else x × pow(x,n-1)² |
| Find Maximum in Array | Easy | "maximum element recursively" | max(arr) = max(arr[0], max(arr[1:])) |
| Majority Element | Easy | "element appearing > n/2 times" | Divide: if same majority in both halves, that is the answer |
Complexity target: O(n log n) for sorting, O(log n) for search, O(n log n) for most divide-and-conquer.
Recognition signal: "O(log n)", "divide in half", "sort and merge", "closest/farthest pair". The problem naturally halves at each level.
Pattern 3: Tree Recursion
Problems defined on tree data structures where the answer for a node depends on the answers for its children. Nearly every tree problem is recursive by nature.
| Problem | Difficulty | Recognition Clue | Key Insight |
|---|---|---|---|
| Maximum Depth of Binary Tree | Easy | "height of tree" | 1 + max(depth(left), depth(right)), base null→0 |
| Diameter of Binary Tree | Medium | "longest path between any two nodes" | Max of: left_depth + right_depth at each node |
| Balanced Binary Tree | Easy | "is tree height-balanced" | Height diff ≤ 1 at every node; use -1 sentinel for unbalanced |
| Same Tree | Easy | "are two trees identical" | Nodes must match AND left subtrees AND right subtrees |
| Symmetric Tree | Easy | "is tree a mirror image of itself" | isMirror(left, right): values match AND cross-subtrees match |
| Path Sum | Easy | "does any root-to-leaf path sum to target" | Subtract node value each level; base: leaf AND remaining==0 |
| Path Sum II (all paths) | Medium | "find all root-to-leaf paths summing to target" | Backtrack: add node, recurse, remove on return |
| Lowest Common Ancestor | Medium | "deepest node that is ancestor of both p and q" | Both in left? recurse left. Both in right? recurse right. Else current node |
| Invert Binary Tree | Easy | "flip tree horizontally" | Swap left and right, then recurse into each |
| Flatten Tree to Linked List | Medium | "flatten tree into right-skewed list in-place" | Post-order: flatten left, flatten right, stitch together |
| Count Good Nodes | Medium | "node is good if no larger value on path from root" | Pass max-so-far down; count if node.val ≥ max |
| Sum Root to Leaf Numbers | Medium | "integers formed by root-to-leaf paths" | Pass accumulated number down; at leaf add to total |
Complexity target: O(n) time (visit every node once), O(h) space where h = tree height.
Recognition signal: "binary tree", "height/depth/balance", "path sum", "compare two trees", "ancestor". Any problem about a tree node's relationship to its subtrees.
Pattern 4: Backtracking — Generate All Solutions
Explore all possible arrangements, subsets, or paths by making a choice, recursing, and undoing the choice. The recursion tree has one branch per choice at each decision point.
| Problem | Difficulty | Recognition Clue | Key Insight |
|---|---|---|---|
| Subsets | Medium | "all subsets of a set" | At each index: include or exclude; 2^n leaf paths |
| Permutations | Medium | "all permutations of a list" | Swap current with each subsequent; recurse; swap back |
| Combinations | Medium | "all combinations of k elements from n" | Include current, recurse for k-1; skip current, recurse |
| Letter Combinations of Phone Number | Medium | "all words from digit mapping" | For each digit, branch into its letters |
| Word Search | Medium | "does word exist in 2D grid" | DFS: mark visited, recurse in 4 directions, unmark |
| N-Queens | Hard | "place n queens so none attack each other" | Place queen in each column of current row; check conflicts |
| Sudoku Solver | Hard | "fill sudoku grid" | Try digits 1-9 in each empty cell; backtrack if invalid |
| Generate Parentheses | Medium | "all valid parenthesis combinations for n pairs" | Add '(' if open_count < n; add ')' if close < open |
| Palindrome Partitioning | Medium | "all ways to partition string into palindromes" | Try every prefix; if palindrome, recurse on suffix |
| Combination Sum | Medium | "all combinations summing to target (reuse allowed)" | Include current, recurse with same index; skip, advance index |
| Combination Sum II | Medium | "all combinations summing to target (no reuse)" | Sort; skip duplicates at same recursion level |
| Restore IP Addresses | Medium | "all valid IP addresses from digit string" | Branch on segment lengths 1,2,3; validate each segment |
Complexity target: O(2ⁿ) for subsets, O(n!) for permutations, problem-specific for others.
Recognition signal: "all possible", "generate all", "find all combinations/permutations/arrangements", "does any path exist". The word "all" or the need to explore every option is the trigger.
Pattern 5: Structural Recursion — Nested Data
The problem's data is recursively structured (nested lists, linked lists, expression trees). The base case is the atom of the structure.
| Problem | Difficulty | Recognition Clue | Key Insight |
|---|---|---|---|
| Flatten Nested List | Medium | "convert nested list to flat list" | Base: not a list (atom) → return [atom]; else recurse into each element |
| Evaluate Reverse Polish Notation | Medium | "evaluate expression in postfix" | Stack-based, but recursion on expression tree is clean |
| Decode String | Medium | "expand k[encoded_string] recursively" | Base: no brackets; Recursive: decode inner string, repeat k times |
| Nested List Weight Sum | Medium | "each integer multiplied by its nesting depth" | Pass depth as parameter; base: integer → depth × val |
| Parse Nested List | Hard | "parse '[1,[4,[6]]]' into nested structure" | Recursive descent parser — base: digit; recursive: '[', recurse, ']' |
| Reverse Linked List | Easy | "reverse singly linked list recursively" | f(head) = f(head.next) then set head.next.next = head, head.next = null |
| Merge Two Sorted Lists | Easy | "merge two sorted linked lists recursively" | Base: either null → return other; pick smaller head, recurse on rest |
| Copy List with Random Pointer | Hard | "deep copy linked list with random pointers" | HashMap of original → copy; recurse on next and random |
Complexity target: O(n) where n is the total number of elements across all nesting levels.
Recognition signal: "nested", "linked list", "parse", "expand", "decode", "deep copy". The structure mirrors the recursion — the recursive call processes a structurally smaller version.
Pattern 6: Mathematical and Number Recursion
Problems where the recursion is defined by mathematical relationships — number theory, sequences, geometric progressions.
| Problem | Difficulty | Recognition Clue | Key Insight |
|---|---|---|---|
| Fibonacci Number | Easy | "nth Fibonacci number" | f(n) = f(n-1) + f(n-2); use memoisation for O(n) |
| Climbing Stairs | Easy | "ways to climb n stairs taking 1 or 2 steps" | f(n) = f(n-1) + f(n-2) — identical to Fibonacci |
| Jump Game (recursive) | Medium | "can you reach the last index" | f(i) = OR over reachable j > i: f(j) |
| Tower of Hanoi | Medium | "move n disks from A to C using B" | Move n-1 from A→B, move disk n from A→C, move n-1 from B→C |
| K-th Symbol in Grammar | Medium | "kth symbol in nth row of grammar pattern" | Parent at position ⌈k/2⌉ in row n-1; if parent=0: child=k%2; if parent=1: child=1-k%2 |
| Gray Code | Medium | "binary reflected Gray code sequence" | G(n) = 0-prefixed G(n-1) + 1-prefixed reversed G(n-1) |
| Integer to English Words | Hard | "convert integer to English words recursively" | Recurse on chunks of 3 digits with appropriate suffix (thousand, million) |
| Perfect Squares | Medium | "minimum number of perfect squares summing to n" | f(n) = 1 + min over squares s≤n: f(n-s); use memoisation |
Complexity target: O(n) with memoisation for sequence problems, O(2ⁿ) without.
Recognition signal: "nth term of sequence", "number of ways to", "how many", "convert number". A mathematical recurrence relationship is explicit or implicit in the problem statement.
Recommended Study Order
Week 1 — Linear and Divide and Conquer Pattern 1 (Linear): Reverse String, Sum Digits, Check Palindrome Pattern 2 (D&C): Binary Search, Merge Sort, Exponentiation Week 2 — Tree Recursion Pattern 3 (Tree): Max Depth, Same Tree, Path Sum, Invert Tree Pattern 3 (Hard): Diameter, Balanced, Lowest Common Ancestor Week 3 — Backtracking Pattern 4 (Easy): Subsets, Combinations, Letter Combinations Pattern 4 (Medium): Permutations, Generate Parentheses, Combination Sum Week 4 — Advanced Patterns Pattern 5 (Structural): Merge Two Lists, Flatten Nested, Decode String Pattern 6 (Math): Fibonacci (memoised), Tower of Hanoi, Climbing Stairs Pattern 4 (Hard): N-Queens, Sudoku Solver, Word Search
Problem-Solving Checklist for Recursion Problems
Before writing code, answer these questions:
1. IDENTIFY THE BASE CASE □ What is the smallest input where the answer is known directly? □ Is this base case reachable from every valid input? □ Are all boundary cases handled? (empty array, null, n=0, n=1) 2. IDENTIFY THE RECURSIVE CASE □ What smaller version of the problem does this call? □ Is the argument strictly closer to the base case? □ How is the sub-call's result combined with the current level? 3. DETERMINE THE PATTERN □ Single chain → linear recursion □ Split in half + merge → divide and conquer □ Tree node + children → tree recursion □ Build and undo choices → backtracking □ Nested structure → structural recursion 4. ESTIMATE COMPLEXITY □ How many recursive calls per node? □ How deep is the recursion tree? □ How much work per node (excluding sub-calls)? □ Total = nodes × work-per-node OR levels × work-per-level 5. CHECK FOR OPTIMISATION □ Are sub-problems repeated? → memoisation (top-down DP) □ Is the recursive call in tail position? → iterative loop □ Is recursion depth > ~1000 (Python) or ~10000 (Java)? → iterative with explicit stack 6. EDGE CASES □ Empty input (array, string, tree null) □ Single element □ All elements identical □ Maximum depth inputs (large n)
Pattern Recognition Quick Reference
| Signal in Problem | Pattern | Structure |
|---|---|---|
| "recursively compute", "find nth" | Linear recursion | f(n) = combine(n, f(n-1)) |
| "sort", "find in sorted", "O(log n)" | Divide and conquer | f(n) = merge(f(left), f(right)) |
| "binary tree", "height/depth/path" | Tree recursion | f(node) = combine(node, f(left), f(right)) |
| "all subsets/permutations/paths" | Backtracking | for choice: pick, f(smaller), unpick |
| "nested list/structure", "parse" | Structural | Base = atom; recursive = container |
| "nth Fibonacci", "number of ways" | Math recurrence | f(n) = f(n-1) + f(n-2) with memoisation |
| "linked list reverse/merge" | Linear structure | Base = null; recursive = head + f(rest) |
| "word search", "grid path" | 2D backtracking | Mark visited, 4-direction DFS, unmark |
Difficulty Progression Reference
| Stage | Characteristics | Representative Problems |
|---|---|---|
| Foundation | Single base case, one recursive call | Factorial, Reverse String, Max Depth Tree |
| Early Intermediate | Two recursive calls, tree structure | Fibonacci (memoised), Same Tree, Path Sum |
| Intermediate | Backtracking, divide and conquer | Subsets, Merge Sort, Generate Parentheses |
| Late Intermediate | Multi-level backtracking, structural | Combination Sum, Palindrome Partition, Decode String |
| Advanced | Hard backtracking, complex tree, classic hard | N-Queens, Sudoku, Closest Pair, Word Search |
Common Interview Topics by Company Focus
Product-based companies (FAANG-tier):
- ›Subsets, Permutations, Combinations — backtracking fundamentals
- ›Binary Tree problems (max depth, diameter, LCA) — tree recursion
- ›Merge Sort / QuickSelect — divide and conquer
- ›Word Search — 2D backtracking
- ›Generate Parentheses — constrained backtracking
Service-based and on-campus:
- ›Fibonacci (memoised), Tower of Hanoi — math recursion
- ›Reverse Linked List, Merge Two Sorted Lists — structural recursion
- ›Binary Search recursive — divide and conquer
- ›Factorial, Palindrome Check — linear recursion
Startup and mid-size tech:
- ›N-Queens, Sudoku Solver — constraint satisfaction backtracking
- ›Decode String, Flatten Nested List — structural recursion
- ›Climbing Stairs, Jump Game — math recurrence with memoisation
- ›Invert / Flatten Binary Tree — tree recursion
Progress Tracking
After solving each problem, record:
- ›Pattern — which of the 6 patterns does this belong to?
- ›Base case(s) — what were they and were any non-obvious?
- ›Recursive case — how did it make progress toward the base?
- ›Tree shape — linear, branching, balanced, unbalanced?
- ›Complexity — what is O(time) and O(space)? How was it derived from the tree?
- ›Optimisation — did repeated subproblems suggest memoisation or iteration?
The decisive signal that pattern recognition is working: when you see "generate all valid parentheses" and immediately think "backtracking — two choices per position (open or close), pruned by open_count ≤ n and close_count ≤ open_count" before reading the constraints.
A problem asks to 'generate all combinations' or 'find all subsets'. Which recursion pattern applies?