AiTechWorlds
AiTechWorlds
University learning companion
Algorithms · Recursion: Functions That Call Themselves
A delivery service must process more stops as its business grows. Apply this topic to a small input first, record the intermediate state, establish why the result is correct, and then estimate what happens when the input becomes ten times larger.
Use this course-level map to place “Recursion: Functions That Call Themselves” in the larger system. It complements—not replaces—a topic-specific worked diagram.
Recursion is when a function solves a problem by calling itself on a smaller version of the same problem, until it reaches a base case simple enough to answer directly.
Picture a set of Matryoshka dolls the famous Russian nesting dolls. You hold the largest one in your hand. You open it. Inside is a slightly smaller doll. You open that one. Inside is another. And another. Until finally you reach the smallest doll, a solid piece of wood that contains nothing.
To find the innermost doll, you follow a pattern:
That's recursion. A problem defined in terms of a smaller version of itself, applied repeatedly until you reach a point simple enough to answer directly.
Every recursive algorithm has this same DNA. And understanding that DNA is the key to writing and reading recursive code fluently.
Every recursive function MUST have exactly these two things:
The base case is the simplest version of the problem, one you can answer directly without recursing further. It's the solid innermost doll.
Without a base case, the function calls itself forever a stack overflow.
The recursive case breaks the current problem into a smaller version of the same problem, then calls itself on that smaller version.
The key rule: each recursive call must move closer to the base case.
When a function calls itself, Python (and every language) uses a call stack a pile of active function frames.
factorial(4)
└── factorial(3)
└── factorial(2)
└── factorial(1)
└── returns 1 ← base case
returns 1 * 1 = 1
returns 2 * 1 = 2
returns 3 * 2 = 6
returns 4 * 6 = 24Each frame holds that call's local variables and waits while inner calls resolve. When the base case returns, the stack unwinds each frame uses its inner call's result to compute and return its own result.
def factorial(n):
# Base case: 0! = 1 and 1! = 1
if n <= 1:
return 1
# Recursive case: n! = n * (n-1)!
return n * factorial(n - 1)
print(factorial(5))
# Output: 120Execution trace for factorial(5):
factorial(5) = 5 * factorial(4)
factorial(4) = 4 * factorial(3)
factorial(3) = 3 * factorial(2)
factorial(2) = 2 * factorial(1)
factorial(1) = 1 ← base case
= 2 * 1 = 2
= 3 * 2 = 6
= 4 * 6 = 24
= 5 * 24 = 120Complexity: O(n) time, O(n) space (n stack frames)
def fib(n):
# Base cases
if n == 0: return 0
if n == 1: return 1
# Recursive case
return fib(n - 1) + fib(n - 2)
print(fib(6))
# Output: 8
# Sequence: 0, 1, 1, 2, 3, 5, 8, ...WHY IT'S SLOW the problem:
fib(5)
├── fib(4)
│ ├── fib(3)
│ │ ├── fib(2)
│ │ │ ├── fib(1) → 1
│ │ │ └── fib(0) → 0
│ │ └── fib(1) → 1
│ └── fib(2) ← REPEATED WORK
│ ├── fib(1) → 1
│ └── fib(0) → 0
└── fib(3) ← REPEATED AGAIN
├── fib(2)
│ ├── fib(1) → 1
│ └── fib(0) → 0
└── fib(1) → 1fib(3) is computed twice, fib(2) is computed three times. For fib(50), this explodes to O(2ⁿ) over a trillion operations. This motivates memoization (dynamic programming), covered in a later lesson.
def sum_list(arr):
# Base case: empty list sums to 0
if len(arr) == 0:
return 0
# Recursive case: first element + sum of rest
return arr[0] + sum_list(arr[1:])
print(sum_list([1, 2, 3, 4, 5]))
# Output: 15Trace:
sum_list([1,2,3,4,5])
= 1 + sum_list([2,3,4,5])
= 1 + 2 + sum_list([3,4,5])
= 1 + 2 + 3 + sum_list([4,5])
= 1 + 2 + 3 + 4 + sum_list([5])
= 1 + 2 + 3 + 4 + 5 + sum_list([])
= 1 + 2 + 3 + 4 + 5 + 0 = 15def power(base, exp):
# Base case: anything to the power 0 is 1
if exp == 0:
return 1
# Recursive case: base^exp = base * base^(exp-1)
return base * power(base, exp - 1)
print(power(2, 8))
# Output: 256Bonus insight: A smarter version halves the exponent each step base^exp = (base^(exp//2))² reducing this to O(log n) time. This is called fast exponentiation.
def infinite_recursion(n):
return infinite_recursion(n + 1) # No base case!
infinite_recursion(0)
# RecursionError: maximum recursion depth exceededPython's default recursion limit is 1000 frames. Exceed it and your program crashes. This is why every recursive function must have a base case that is always reachable.
Common mistake base case exists but is never reached:
def countdown(n):
if n == 0: return # base case
return countdown(n + 1) # Bug: goes UP, never reaches 0| Aspect | Recursion | Iteration |
|---|---|---|
| Readability | Natural for tree/graph problems | Natural for sequential problems |
| Memory | Uses call stack (O(depth)) | Usually O(1) extra space |
| Speed | Function call overhead | Slightly faster in practice |
| Risk | Stack overflow on deep input | No stack risk |
| Best for | Trees, graphs, divide and conquer | Arrays, simple loops |
Any recursive function can be rewritten iteratively (using an explicit stack). Some are much cleaner recursively.
Recursion shines when the data structure itself is recursive:
Binary Tree: Directory system:
5 /home
/ \ ├── user/
3 8 │ ├── docs/
/ \ \ │ └── photos/
1 4 9 └── tmp/Both a binary tree and a file system are defined as: a node containing smaller nodes of the same type. Recursive traversal writes itself almost automatically.
def tree_sum(node):
if node is None: # base case: empty node
return 0
return node.val + tree_sum(node.left) + tree_sum(node.right)This would take a complex loop with an explicit stack to replicate iteratively.
When writing a recursive function, don't try to mentally trace every call. Instead:
For factorial(n):
if n <= 1: return 1factorial(n-1) correctly returns (n-1)!return n * factorial(n-1)Trust the recursion. The math handles the rest.
Next lesson: Backtracking recursion with the power to undo decisions.
University learning companion
Algorithms · Recursion: Functions That Call Themselves
Complete the steps without looking at the answer first; record evidence rather than only a final result.
These are primary or established learning references for this subject. Check the relevant chapter or specification for the exact topic and version.
Get this course's notes on Telegram!
Free cheat sheets, summaries & practice exercises