Recursion is a technique where a function calls itself to solve a smaller version of the same problem. It is a core concept in algorithms, data structures, and problem solving.
In this chapter you will learn
Recursion happens when a function calls itself to solve a smaller part of a problem.
A recursive function must have:
Recursion is especially useful for problems with:
Real-life analogies:
This function prints a countdown from n to 1, then prints "Done!". It uses recursion to
reduce the problem size with each call.
Output for countdown(5):
Factorial is defined as:
We can define factorial recursively as:
n! = n × (n-1)! for n > 11! = 1 is the base casedef factorial(n):
if n == 1: # Base case
return 1
return n * factorial(n - 1) # Recursive case
factorial(5) worksEach call reduces n by 1 until it reaches the base case:
The Fibonacci sequence is defined as:
def fib(n):
if n <= 1: # Base cases
return n
return fib(n - 1) + fib(n - 2) # Recursive case
fib(4)
This version is simple but inefficient for large n because it recomputes many values.
(You typically optimize it with memoization or iteration.)
Many recursive algorithms can also be written using loops.
| Feature | Recursion | Iteration |
|---|---|---|
| Code readability | Often more elegant | Sometimes more verbose |
| Memory usage | Higher (call stack) | Lower |
| Performance | Can be slower in Python | Usually faster |
| Best for | Trees, graphs, divide & conquer | Simple loops, linear scans |
Tail recursion is a special case where the recursive call is the last operation in the function. Some languages optimize tail calls to avoid growing the call stack.
A function is tail recursive if the recursive call is the last operation in the function.
def factorial_tail(n, acc=1):
if n == 1:
return acc
return factorial_tail(n - 1, acc * n)
In some languages, tail recursion is optimized to avoid extra stack frames. Python does NOT perform tail recursion optimization, so deep recursion can still overflow.
import sys
print(sys.getrecursionlimit()) # often 1000 by default
You can increase this limit, but it is risky:
import sys
sys.setrecursionlimit(5000) # Use with caution!
Deep recursion can lead to crashes. For very deep problems, consider iterative solutions or using data structures like stacks.
Always ensure your recursive functions have proper base cases to prevent infinite recursion.
Test code here: (It will show that the code is valid but no output will be displayed)
Recursion is a natural fit for traversing hierarchical data structures like trees and graphs.
In-order traversal visits the left subtree, the node, then the right subtree:
def inorder(node):
if not node:
return
inorder(node.left)
print(node.value)
inorder(node.right)
DFS explores as far as possible along each branch before backtracking:
def dfs(graph, node, visited=None):
if visited is None:
visited = set()
if node in visited:
return
visited.add(node)
print(node)
for neighbor in graph[node]:
dfs(graph, neighbor, visited)
Many efficient sorting algorithms use recursion to divide and conquer the problem.
Merge sort divides the array into halves, sorts each half recursively, then merges them:
def merge_sort(arr):
if len(arr) <= 1:
return arr
mid = len(arr) // 2
left = merge_sort(arr[:mid])
right = merge_sort(arr[mid:])
# merge two sorted halves
result = []
i = j = 0
while i < len(left) and j < len(right):
if left[i] < right[j]:
result.append(left[i])
i += 1
else:
result.append(right[j])
j += 1
result.extend(left[i:])
result.extend(right[j:])
return result
Quick sort selects a pivot, partitions the array, then sorts the partitions recursively:
def quick_sort(arr):
if len(arr) <= 1:
return arr
pivot = arr[0]
left = [x for x in arr[1:] if x < pivot]
right = [x for x in arr[1:] if x >= pivot]
return quick_sort(left) + [pivot] + quick_sort(right)
Common pitfalls when writing recursive functions include:
For any recursive problem, follow this pattern:
General template:
def solve(problem):
# 1. Base case
if is_simple(problem):
return simple_answer(problem)
# 2. Reduce the problem
subproblem = make_smaller(problem)
# 3. Recursive call
subresult = solve(subproblem)
# 4. Combine result
return combine(problem, subresult)
Here are some common recursive exercises to practice your skills:
pow(x, n)These problems are very common in interviews and are great practice for mastering recursion.
In this guide, you learned:
Recursion is a powerful tool in your programming toolbox. With practice, it becomes an intuitive way to think about complex, structured problems.