Algorithm

Factorial

Recursion Pattern

Factorial

Given a non-negative integer n, return n! (n factorial) — the product of every integer from 1 up to n. By definition the empty product 0! = 1. Return the single integer result. This is the entry point to recursion, so the goal is not just the number but learning to see the problem as a smaller copy of itself.

CONSTRAINTS
  • 0 <= n <= 12
  • n is an integer
  • 12! = 479001600 still fits in a 32-bit signed integer; beyond that it overflows
EXAMPLE 1
Input: n = 0
Output: 1
The empty product is defined as 1. This is the base case — no multiplication happens at all.
EXAMPLE 2
Input: n = 1
Output: 1
1! = 1. Also handled by the base case, so the function returns without recursing.
EXAMPLE 3
Input: n = 3
Output: 6
3 × 2 × 1 = 6. The three multiplications resolve on the way back up the call stack.
EXAMPLE 4
Input: n = 5
Output: 120
5 × 4 × 3 × 2 × 1 = 120.
What should factorial(0) return?
1. The factorial of 0 is defined as the empty product, which is 1 — not 0. Getting this base case wrong is the classic bug; if the base returned 0, every answer would collapse to 0.
Can n be negative?
Factorial is only defined for non-negative integers, and the constraints guarantee n ≥ 0. Worth confirming in an interview: a negative n with this code would recurse past the base case forever and overflow the stack.
Recursion or a loop — does it matter here?
For factorial a loop is equally correct and uses no call-stack space. Recursion is taught here because the *way of thinking* generalizes to problems where a loop is awkward; it is not an optimization.
Why does the answer overflow so quickly?
Factorials grow explosively — 13! already exceeds a 32-bit integer. The constraint caps n at 12 so the result stays representable.

Factorial asks for a simple product: 5! means 5 × 4 × 3 × 2 × 1 = 120. You could stop reading and write a loop. But this problem is the doorway to recursion, a way of solving a problem by having it solve a smaller version of itself, so we will use it to build the whole mental model from scratch — the pieces here reappear in trees, sorting, backtracking, and dynamic programming.

The loop you already know

The obvious way is to walk from 1 to n and keep multiplying:

python
result = 1
for i in range(2, n + 1):
    result *= i
return result

Nothing is wrong with this. It is O(n) time and uses no extra memory. Keep it in mind as the honest baseline — recursion will not make factorial faster. What recursion gives us is a different way of thinking that pays off on problems a loop cannot express cleanly.

The reframe: define the answer in terms of a smaller answer

Look at the definition again and notice the self-similarity hiding in it:

5! = 5 × (4 × 3 × 2 × 1) = 5 × 4!

The parenthesized part is 4!. In general, n! = n × (n − 1)!. The problem contains a smaller copy of itself. That is the signal for recursion: whenever the answer for n can be built from the answer for something smaller than n, you can write a function that calls itself.

Two things must be true for this to actually terminate, and they are the two halves of every recursive function:

- The base case — the smallest input you can answer without recursing. Here it is n = 0 (and n = 1): 0! = 1, full stop. Without a base case the calls never stop and the program crashes.
- The recursive case — reduce the problem to a strictly smaller one and combine. Here: return n × factorial(n − 1). "Strictly smaller" matters: each call must move toward the base case, or it will never arrive.
python
def factorial(n):
    if n <= 1:            # base case: smallest answerable input
        return 1
    return n * factorial(n - 1)   # recursive case: shrink toward the base
The leap of faith, and why it is not really faith

The hardest part for a beginner is the line return n factorial(n - 1). It seems to use the answer before we have computed it. The trick is to trust the smaller call: assume* factorial(n − 1) already returns the correct value, and your only job is to use it correctly for n. You do not trace the whole thing in your head — you check two things and stop: (1) does the base case return the right answer, and (2) if the smaller call is correct, is my combination correct? If both hold, the function is correct for every n by induction, the same domino logic as mathematical proof: the base case knocks over the first domino, and the recursive case guarantees each domino knocks over the next.

What the machine actually does: the call stack

"Faith" is really bookkeeping, and the bookkeeper is the call stack. Every time a function is called, the computer pushes a frame onto the stack holding that call's own local variables and its unfinished work — for factorial, the frame remembers "I still owe a multiplication by n once the smaller call comes back." The frame waits, frozen, until the call it is waiting on returns. Frames pile up on the way down and unwind on the way back up:

Worked Example:factorial(3)
- Call factorial(3). n > 1, so it needs factorial(2) first. Frame for 3 is pushed and waits, owing "× 3".
- Call factorial(2). Needs factorial(1). Frame for 2 is pushed, owing "× 2".
- Call factorial(1). n ≤ 1 — base case. Returns 1 immediately, nothing pushed further.
- Now unwind: frame 2 receives 1, computes 2 × 1 = 2, returns it. Frame 3 receives 2, computes 3 × 2 = 6, returns it.
- Answer: 6. Notice the multiplications happen on the way back up, in reverse order of the calls — this is called head recursion (the work comes after the recursive call, on the return trip).

The stack is why recursion costs O(n) space here even though the loop cost none: at the deepest point, n frames are stacked at once. Push n too high and you get a stack overflow — the stack runs out of room. That is the price of the cleaner formulation, and the reason deep recursions are sometimes rewritten as loops.

The pattern, extracted

Every recursive solution you will ever write fills the same three slots:

- (a) Base case — the input small enough to answer directly. (n ≤ 1 → return 1)
- (b) Progress — how each call shrinks toward the base. (recurse on n − 1)
- (c) Combine — how to build this answer from the smaller one. (multiply by n)

Solving a new recursive problem is nothing more than deciding those three things. Recognition signals for reaching for recursion: the problem is defined in terms of itself (n! uses (n−1)!), or it branches into sub-problems (explore left and right, try each choice), or it lives on a naturally recursive structure (a tree, a nested list). The two ways it breaks — and the two bugs to check first — are a missing or wrong base case (infinite recursion → stack overflow) and a recursive call that does not strictly shrink the input (so the base case is never reached). Train the reflex: before writing the recursive line, write the base case, and make sure every call moves toward it. Everything after this — linear recursion on a single call (Sum of Digits), binary recursion that branches (Fibonacci), and divide-and-conquer that halves (Power) — is this same skeleton with different slots filled.

O(n) Iterative Loop
O(n) Time · O(n) Stack Linear Recursion