Recursion
A problem that contains smaller copies of itself can be solved by a function that calls itself — once it clicks, whole classes of problems get simpler.
The problem
You need to multiply 5 × 4 × 3 × 2 × 1 to get 5!. Easy. Now do 100!. You are not going to write a hundred multiplications by hand, and a loop feels like overkill for something you can describe in one breath.
Here is the breath: 5! is just 5 × 4!. And 4! is just 4 × 3!. Every factorial is a slightly smaller factorial waiting to happen. The problem literally contains a smaller copy of itself. Can your code say that as plainly as you just did?
A first attempt
Reach for a loop and you get something that works but hides the idea:
def factorial(n):
result = 1
for i in range(2, n + 1):
result *= i
return resultNothing is wrong here — it runs in O(n) time and O(1) space. But you had to invent a result accumulator and a counter i that have nothing to do with the definition of a factorial. The loop describes how to grind it out, not what a factorial is. When the structure of a problem is self-similar, a loop forces you to flatten that structure by hand.
The insight
A function is allowed to call itself. If factorial(5) can ask factorial(4) for its answer and just multiply by 5, you never have to manage a counter at all. You describe the problem in terms of a smaller version of the same problem.
Two things make this safe rather than an infinite loop:
- A base case — the smallest input you can answer with no further help.
factorial(1)is1. - A recursive case — everything else, expressed using a smaller input that marches toward the base case.
That is recursion: a function defined in terms of itself, anchored by a base case.
How it works
Anchor the base case
Decide the smallest input you can answer directly. For factorial, n <= 1 returns 1. Without this anchor the calls never stop and the call stack overflows.
Shrink toward it
Express the answer using a strictly smaller input: factorial(n) = n * factorial(n - 1). Each call must move closer to the base case, or you never arrive.
Trust the recursion
Assume factorial(n - 1) already returns the right answer. You only have to combine it with n. This leap of faith is the whole skill — you reason about one layer, not the whole tower.
Watch the stack unwind
Calls pile up until the base case, then resolve back down:
factorial(4)
= 4 * factorial(3)
= 3 * factorial(2)
= 2 * factorial(1)
= 1 <- base case
= 2 * 1 = 2
= 3 * 2 = 6
= 4 * 6 = 24The code
def factorial(n):
if n <= 1: # base case
return 1
return n * factorial(n - 1) # recursive case
print(factorial(5)) # 120function factorial(n: number): number {
if (n <= 1) return 1; // base case
return n * factorial(n - 1); // recursive case
}
console.log(factorial(5)); // 120static long factorial(int n) {
if (n <= 1) return 1; // base case
return n * factorial(n - 1); // recursive case
}
// factorial(5) -> 120long factorial(int n) {
if (n <= 1) return 1; /* base case */
return n * factorial(n - 1); /* recursive case */
}
/* factorial(5) -> 120 */long long factorial(int n) {
if (n <= 1) return 1; // base case
return (long long)n * factorial(n - 1); // recursive case
}
// factorial(5) -> 120Complexity
| Measure | Cost | Why |
|---|---|---|
| Time | O(n) | One multiplication per level, n levels deep. |
| Space | O(n) | Each pending call keeps a stack frame until it resolves. |
Note the hidden cost: the iterative loop was O(1) space, but recursion pays O(n) for the call stack. Elegance is not free.
When to use it
Reach for recursion when the problem is self-similar
If you can describe the answer in terms of a smaller version of the same problem — trees, nested folders, divide-and-conquer — recursion mirrors the structure and reads beautifully. Avoid it for simple linear counting where a loop is clearer and cheaper, and beware deep recursion in languages without tail-call optimization: a chain of ~10,000 calls can overflow the stack.
Practice
Recap
- A recursive function solves a problem by calling itself on a smaller input.
- Every recursion needs a base case to stop and a recursive case that shrinks toward it.
- Recursion trades O(n) stack space for code that mirrors a self-similar problem.
How is this guide?
Last updated on