Intermediate: core algorithms · Recursion and backtracking · topic 21 of 61 · 35 min
Recursion: functions that call themselves
Explanation
Why recursion?
Some problems are easiest to describe in terms of a smaller copy of themselves. The factorial of 5 is 5 times the factorial of 4. The size of a folder is the size of its files plus the size of each folder inside it. A path through a grid is one step plus a path through a smaller grid. Recursion lets you write the code exactly the way you describe the problem, and it is the base for everything else in this module: divide and conquer, backtracking and memoization. Trees (Module 7) and graphs (Module 9) use it all the time.
Base case and recursive case
A recursive function is a function that calls itself. It has two parts. The base case is an input so small that you return the answer directly, without calling again. The recursive case makes the problem smaller, calls the function on the smaller problem, and uses that answer to build its own. If the input never reaches the base case, the calls never stop.
def factorial(n):
if n <= 1: # base case
return 1
return n * factorial(n - 1) # recursive case: a smaller n
print(factorial(4)) # Output: 24
print(factorial(0)) # Output: 1The call stack, step by step
When a function calls another function, the caller pauses and waits. Python keeps every paused call in a stack frame: a small record of its local variables and where to continue. The frames form the call stack, a stack in the sense of Module 4: the last call made is the first to finish. For factorial(4), four frames pile up before any multiplication happens; then they finish in reverse order.
factorial(4) waits for factorial(3)
factorial(3) waits for factorial(2)
factorial(2) waits for factorial(1)
factorial(1) returns 1 base case, the stack is 4 frames deep
factorial(2) returns 2 * 1 = 2
factorial(3) returns 3 * 2 = 6
factorial(4) returns 4 * 6 = 24Time is the number of calls times the work per call: n calls with O(1) work each, so O(n). Space is the deepest the stack gets: n frames, so O(n), even though no list is created. Module 1 showed this with a recursive sum; it is true of every recursive function.
The recursion limit and RecursionError
Every frame takes memory, so Python sets a limit on how deep the stack may grow. sys.getrecursionlimit() returns 1000 by default. Going deeper raises RecursionError: maximum recursion depth exceeded. So factorial(5000) written recursively fails, while a loop handles it easily. You can raise the limit with sys.setrecursionlimit(), but very deep recursion still uses a lot of memory and can crash, so it is a last resort. JavaScript has a similar limit (in Chrome and Node.js the error is RangeError: Maximum call stack size exceeded); how deep you can go depends on the engine and the size of each frame.
Recursion or a loop?
Anything written with recursion can be written with a loop, sometimes with an explicit stack of your own. When the recursion only goes one way, like factorial or summing digits, a loop is simpler and uses O(1) space. Recursion shines when a call branches into several smaller calls (trees, subsets, divide and conquer), where the loop version needs a hand-made stack and is harder to read. In an interview, write whichever is clearer and say the depth: "this recursion goes n deep, so it uses O(n) stack space".
Tail recursion
A call is a tail call when it is the very last thing the function does, with nothing left to compute after it returns. In factorial above, the multiplication happens after the call returns, so it is not a tail call. Moving the running product into a parameter (an accumulator) makes it one. Some languages (Scheme, and many functional languages) reuse the same frame for a tail call, so tail recursion runs in O(1) space. Python never does this, by design, and most JavaScript engines (Chrome, Node.js and Firefox) do not either; Safari is the exception. So in Python, tail recursion still fails at about 1,000 levels; turn it into a loop instead.
def fact_tail(n, acc=1):
if n <= 1:
return acc
return fact_tail(n - 1, acc * n) # a tail call, but Python still adds a frame
def fact_loop(n):
acc = 1
for k in range(2, n + 1):
acc *= k
return acc
print(fact_tail(5), fact_loop(5)) # Output: 120 120How to write a recursive function
- Say in words what the function returns, for example "count_paths(r, c) is the number of paths in an r × c grid".
- Find the smallest inputs and their answers: these are the base cases.
- Assume the function already works for smaller inputs (this is called the leap of faith) and build the answer for n from them.
- Check that every recursive call moves closer to a base case.
- Trace a tiny input by hand, then state the time (number of calls × work per call) and the space (the deepest stack).
Common mistakes
- No base case, or a base case that is never reached (for example n == 0 when n can be negative). Fix: write the base case first and check every input shrinks towards it.
- Forgetting to return the recursive result: writing factorial(n - 1) instead of return n * factorial(n - 1) gives None. Fix: every path through the function must return something.
- Hidden O(n) work in each call, such as s[1:] slicing, which copies the string. Fix: count it; pass an index instead when it matters.
- Recursing thousands of levels deep in Python. Fix: use a loop, or keep the depth near log n as in divide and conquer.
- Counting time as the depth. Fix: time is the TOTAL number of calls; a function that calls itself twice makes far more calls than its depth.
Need help?
Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.
Try:
Watch the lesson
Recursion: functions that call themselves · 1/8
This lesson is about recursion: a function that solves a problem by calling itself on a smaller piece of it.
Prefer a video by a teacher? Find free videos on “recursion explained call stack base case python beginners” on YouTube ↗
In real life: Measuring a folder on a laptop or in cloud storage
A file manager, or a cloud drive such as the one a school uses for student records, shows how much space a folder takes. A folder holds files and other folders, which hold more files and folders. The natural code is recursive: the size of a folder is the size of its files plus the size of each folder inside it, and an empty folder is the base case. The depth is the deepest nesting of folders, which is usually small, so recursion is safe here.
folder = {
"files": [120, 30],
"folders": [
{"files": [500], "folders": []},
{"files": [10, 20], "folders": [{"files": [5], "folders": []}]},
],
}
def size(f):
total = sum(f["files"])
for sub in f["folders"]:
total += size(sub)
return total
print(size(folder))Practice (0/4 done)
Write sum_digits(n) that returns the sum of the decimal digits of a non-negative integer n, using RECURSION: sum_digits must call itself (the tests check this). Example: sum_digits(1234) returns 10. sum_digits(0) returns 0. Hint: the last digit is n % 10 and the rest is n // 10.
Write power(x, n) that returns x raised to the integer n (n ≥ 0) using the simple recursion x^n = x × x^(n−1), with x^0 = 1. power must call itself (the tests check this); do not use ** or pow. This makes n calls, O(n) time and O(n) stack space; Topic 2 makes it O(log n). Example: power(2, 10) returns 1024.
Write reverse_string(s) that returns s reversed, using recursion: the reverse of s is the reverse of s[1:] followed by s[0]. reverse_string must call itself; do not use s[::-1] or reversed(). Strings in the tests have at most 500 characters. Example: reverse_string("abc") returns "cba".
A robot starts at the top-left cell of a grid with rows × cols cells and must reach the bottom-right cell, moving only RIGHT or DOWN one cell at a time. Write count_paths(rows, cols) that returns the number of different paths, using recursion: the paths into a cell come from the cell above or the cell to the left. A grid with 0 rows or 0 columns has 0 paths; a 1 × 1 grid has 1 (the robot is already there). Grids in the tests are at most 10 × 10 (Topic 5 makes bigger ones fast). Example: count_paths(3, 3) returns 6.
Quick quiz
Read more (free)
Related help
Before this, learn
This topic builds on these. If something here feels new, open them first.
Related topics
- Divide and conquerRecursion and backtracking · same module
- Backtracking: subsets and permutationsRecursion and backtracking · same module
- Backtracking with pruningRecursion and backtracking · same module
- Recursion and sorting algorithmsPython Programming · Algorithms, data structures and testing
- Overloading, default arguments and recursionC++ Programming (modern C++ and DSA) · Decisions, loops and functions
- Higher-order functions and recursionJavaScript Programming (language in depth) · Functions and closures
Questions people ask about this
Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.
What happens if a recursive function has no reachable base case?
Answer: It keeps calling itself until Python raises RecursionError
Every call adds a frame; with no base case the stack grows until the recursion limit (about 1,000 in Python) is hit.
What is the extra space of a recursive function that goes n levels deep and creates no lists?
Answer: O(n)
n frames wait on the call stack at the deepest point, so the space is O(n).
What does sys.getrecursionlimit() return in a fresh Python session?
Answer: 1000
The default limit is 1000 frames; deeper recursion raises RecursionError.
Why does fact_tail(5000) (a tail-recursive factorial) still fail in Python?
Answer: Python does not optimise tail calls, so every call still adds a frame
Python keeps one frame per call even for tail calls, so 5000 levels go past the limit.
def f(n): return 0 if n == 0 else 1 + f(n - 2). What does f(5) do?
Answer: Raises RecursionError, because n jumps from 1 to −1 and never equals 0
5 → 3 → 1 → −1 → −3 … never hits 0. Use n <= 0 as the base case, or check the input.
What happens if a recursive function has no base case?Related
Answer: Python raises RecursionError after about 1000 nested calls
The calls keep stacking up until Python's recursion limit stops them with RecursionError.
What are the two parts every recursive function needs?Related
Answer: A base case and a recursive case that gets closer to it
Without a base case it never stops; without progress it never reaches the base case.
What does every recursive function need to stop?Related
Answer: A base case
The base case answers the smallest input directly without calling itself.
What usually happens when recursion goes too deep in C?Related
Answer: A stack overflow, typically a segmentation fault
The call stack runs out of space.
From Recursion
Pass the quiz (60% or more) or mark this topic as done when you are ready.
Your progress is saved on this device only.
Next steps
- 4 more topics in “Recursion and backtracking” before its exam: Divide and conquer, Backtracking: subsets and permutations, Backtracking with pruning, Memoization: remembering answers.
- Module 5 exam: Recursion and backtracking · 14 questions · 25 min, without help. 5 topics of this module still to finish first.
- Then the next module, Sorting and searching. 61 topics left in the course before the final exam.