Tasks solved with recursion
Recursion is often used for tasks where a large problem splits naturally into subtasks of the same kind. Below are the most typical classes of such problems, with JavaScript examples.
Theory
TL;DR
- Math computations: factorial, Fibonacci numbers, exponentiation.
- Data structure traversal: trees (the DOM, a file system), graphs, DFS.
- Work with arrays: sum, search, filtering via "head plus tail".
- Divide and conquer algorithms: QuickSort, MergeSort, binary search, Tower of Hanoi.
- Processing nested structures: flattening an array, walking arbitrarily nested objects.
- Logic and combinatorial problems: permutations, a path through a maze, the knapsack.
Quick example
// Exponentiation by splitting the task
function pow(x, n) {
if (n === 0) return 1; // base case
return x * pow(x, n - 1); // recursive step
}
console.log(pow(2, 10)); // 1024Math computations
The classic case, because the formula is already stated recurrently.
-
Factorial of a number:
n! = n * (n-1)! -
Fibonacci numbers:
F(n) = F(n-1) + F(n-2) -
Exponentiation by splitting the task:
javascriptfunction pow(x, n) { if (n === 0) return 1; return x * pow(x, n - 1); }
Here the recursive code repeats the mathematical notation almost word for word, which makes it easy to check by eye.
Traversing data structures
A tree is made of subtrees, so traversing a tree is traversing each subtree.
-
Trees (the DOM or a file system, for example):
javascriptfunction traverse(node) { console.log(node.value); node.children.forEach(traverse); } -
Graphs: depth-first search (DFS) is written recursively with almost no effort; breadth-first search (BFS) is usually implemented with a queue, because it goes level by level rather than deep.
For graphs you must remember the visited nodes in a Set, otherwise a cycle in the data loops the traversal forever.
Arrays and nested structures
It is convenient to see an array as "the first element plus the rest of the array".
-
Summing elements, searching, filtering:
javascriptfunction sum(arr) { if (arr.length === 0) return 0; return arr[0] + sum(arr.slice(1)); } -
Flattening an array:
javascriptfunction flatten(arr) { return arr.reduce((acc, val) => acc.concat(Array.isArray(val) ? flatten(val) : val), []); }
Nested structures of unknown depth are the strongest argument for recursion: a loop would have to maintain an explicit stack.
Divide and conquer algorithms
The task is split into several smaller ones, each is solved by the same algorithm, and then the results are combined.
- QuickSort
- MergeSort
- Binary search
- The Tower of Hanoi algorithm
function binarySearch(arr, target, lo = 0, hi = arr.length - 1) {
if (lo > hi) return -1; // base case: nothing left
const mid = (lo + hi) >> 1;
if (arr[mid] === target) return mid;
return arr[mid] < target
? binarySearch(arr, target, mid + 1, hi)
: binarySearch(arr, target, lo, mid - 1);
}Logic and combinatorial problems
- Enumerating all combinations and permutations;
- finding a path through a maze;
- solving "weighted" problems such as the knapsack.
function permutations(items) {
if (items.length <= 1) return [items]; // base case
return items.flatMap((item, i) => {
const rest = [...items.slice(0, i), ...items.slice(i + 1)];
return permutations(rest).map((p) => [item, ...p]);
});
}
console.log(permutations(['a', 'b', 'c']).length); // 6Recursion describes backtracking naturally here: make a choice, go deeper, come back and try the next one.
Common mistakes
- Recursion where a loop would do. A linear pass over an array is simpler with a loop and has no stack depth limit.
arr.slice(1)on a large array. Every call copies the tail, so instead ofO(n)you getO(n^2)in both time and memory; pass an index instead.- A naive
fibwithout memoisation.fib(n - 1) + fib(n - 2)produces an exponential number of calls; a cache or a loop makes it linear. - Graph traversal without a visited set. A cycle in the data turns the traversal into an infinite one.
- Deep recursion over large data. Walking a million elements crashes with
RangeError: Maximum call stack size exceededeven when the logic is correct. - A forgotten base case in combinatorial problems. Most often it is
items.length <= 1or the empty branch of a tree.
Short Answer
Interview readyA concise answer to help you respond confidently on this topic during an interview.