RecursionRecursion
A function calling itself, with the call stack remembering the way back.
Used for: Tree traversal, DFS, divide and conquer, and where DP starts
01Why it exists
A folder holds files and subfolders, those subfolders hold files and subfolders of their own, and there is no telling how deep it goes.
Why this fitsRecursion only describes the rule for one level: my size = my files + the size of each of my subfolders. And how is a subfolder's size worked out? By the same function. How deep the nesting goes never comes into it.
A comment has replies, and those replies have replies of their own; a menu contains submenus. A React component has to draw that structure.
Why this fitsA component that renders itself inside itself is recursion. For any data where a structure contains the same structure, recursion is the most natural thing to write.
Tree traversal, DFS, merge sort, quicksort, backtracking, dynamic programming — every one of them is a variation on recursion.
Why this fitsGet comfortable with the habit of trusting that a smaller copy of yourself returns the right answer, and all those later algorithms become just another problem to take apart the same way.
Reach for it when you see:A structure that contains the same structure, no telling how many levels deep, shrinking the problem a little turns it into the same problem, tree-shaped data.
02The core idea
Recursion is a function calling itself, but the part that actually matters is the way of thinking: take a problem of size n and build its answer out of the answer to a smaller version of the same problem. You are responsible for exactly two things — how to answer the smallest problem directly (the base case), and how to assemble the big answer from the small one (the recursive case).
While the program runs, every call puts a frame on the call stack, remembering this level's arguments and where to return once it has an answer. After the descent reaches the base case, those frames pop one by one, passing the answer back up. That is why recursion costs at least O(depth) space, and why recursing too deep overflows the stack.
What trips up beginners most is trying to trace what every level is doing. Don't. Trust that the recursive call returns the right answer and only check that your own level uses it correctly. This is the recursive leap of faith, and it is mathematical induction in code form.
03The algorithm
- 1Define what the function means:
factorial(n)returns n factorial. State the meaning clearly, because that is what you will later be trusting. - 2Write the base case: the smallest situation, the one you can answer outright.
n == 1returns 1. With no base case the recursion never ends. - 3Write the recursive case: assume
factorial(n - 1)is already correct, and thenfactorial(n)is justn * factorial(n - 1). - 4Check that every recursive call moves toward the base case (n gets smaller, the list gets shorter, the tree goes one level down), or it will never stop.
- 5Estimate the depth: factorial recurses n deep, anything that halves recurses log n deep. When the depth gets too large, switch to iteration or an explicit stack.
04Interactive demo
Step through factorial(4). On the left is the line currently executing; on the right is the call stack, pushing one frame at a time on the way down and returning one answer at a time once the base case is reached.
1def factorial(n):2 if n == 1:3 return 14 return n * factorial(n - 1)
05Code
The three examples cover the three shapes recursion takes — a number shrinking, a list getting shorter, and a tree going one level down — with the iterative version at the end for comparison.
def factorial(n):
if n == 1: # base case: the smallest problem, answered outright
return 1
return n * factorial(n - 1) # recursive case: hand it to a smaller copy of yourself
def total(items):
# Sum of a list: the first element + the sum of the rest
if not items:
return 0
return items[0] + total(items[1:])
def folder_size(folder):
# Folder size = every file in it + every subfolder in it
size = sum(f.size for f in folder.files)
for sub in folder.subfolders:
size += folder_size(sub) # a subfolder has exactly the same shape as this folder
return size
def factorial_iter(n):
# The iterative version of the same thing: no call stack, O(1) space
result = 1
for k in range(2, n + 1):
result *= k
return result06Practice
- LeetCode 344Reverse String (do it recursively)Easy
- LeetCode 509Fibonacci NumberEasy
- LeetCode 206Reverse Linked List (the recursive version)Easy
- LeetCode 70Climbing Stairs (write the recursion first, and feel why it is slow)Easy
- LeetCode 779K-th Symbol in GrammarMedium