Unit 9 • Lesson 1

Recursion and Problem Decomposition

Overview

Recursion allows a function to call itself to solve smaller parts of a problem. You'll learn how to write recursive functions, understand base and recursive cases, and compare recursion to iteration in terms of efficiency and readability, mastering a powerful problem-solving technique.

Intermediate 25–30 min

What You Will Learn in This Lesson

By the end of this lesson, you will know:

  • What recursion is: Functions that call themselves.
  • Base cases: The stopping condition for recursion.
  • Recursive cases: How functions call themselves with smaller problems.
  • Problem decomposition: Breaking problems into smaller, similar subproblems.
  • When to use recursion: Comparing recursion to iteration.

What Is Recursion?

Recursion is when a function calls itself to solve a problem. Instead of using loops, recursive functions break problems into smaller versions of the same problem.

Key Concept

Recursion is like Russian nesting dolls - each doll contains a smaller version of itself. Similarly, recursive functions solve problems by solving smaller versions of the same problem.

Simple Recursive Example: Factorial
def factorial(n):
    # Base case: stop recursion
    if n == 0 or n == 1:
        return 1
    # Recursive case: call function with smaller problem
    return n * factorial(n - 1)

print(factorial(5))  # Output: 120

Two Essential Components

Every recursive function needs two parts:

1

Base Case

The stopping condition that prevents infinite recursion. This is when the problem is small enough to solve directly.

2

Recursive Case

The part that calls the function again with a smaller or simpler version of the problem.

Warning: Missing Base Case

Without a base case, recursion will continue forever (until Python runs out of memory). Always ensure your recursive function has a base case!

How Recursion Works

Let's trace through a recursive function:

Example: Sum of Numbers
def sum_list(numbers):
    # Base case: empty list
    if not numbers:
        return 0
    # Recursive case: first number + sum of rest
    return numbers[0] + sum_list(numbers[1:])

result = sum_list([1, 2, 3, 4])
print(result)  # Output: 10

How It Works

sum_list([1, 2, 3, 4]) calls sum_list([2, 3, 4]), which calls sum_list([3, 4]), which calls sum_list([4]), which calls sum_list([]). The empty list returns 0, then each call adds its number and returns the result back up.

Recursion vs Iteration

Both recursion and loops can solve the same problems:

Recursion

More elegant for some problems, closer to mathematical definitions, can be harder to understand

Iteration (Loops)

More efficient (less memory), easier to understand for many, more Pythonic for simple cases

Same Problem, Two Approaches
# Recursive approach
def count_down_recursive(n):
    if n <= 0:
        print("Done!")
        return
    print(n)
    count_down_recursive(n - 1)

# Iterative approach
def count_down_iterative(n):
    while n > 0:
        print(n)
        n -= 1
    print("Done!")

More Recursive Examples

Let's look at more examples to understand recursion better:

Example: Fibonacci Sequence
def fibonacci(n):
    # Base cases
    if n == 0:
        return 0
    if n == 1:
        return 1
    # Recursive case: sum of previous two numbers
    return fibonacci(n - 1) + fibonacci(n - 2)

# Calculate first 10 Fibonacci numbers
for i in range(10):
    print(f"fib({i}) = {fibonacci(i)}")
Example: Power Function
def power(base, exponent):
    # Base case: any number to the power of 0 is 1
    if exponent == 0:
        return 1
    # Recursive case: base * base^(exponent-1)
    return base * power(base, exponent - 1)

print(power(2, 5))  # Output: 32 (2^5)

Understanding Recursive Calls

When tracing recursive functions:

  1. Start with the original call
  2. Follow each recursive call until you reach the base case
  3. Work backwards, returning values up the call stack
  4. Each recursive call works on a smaller problem

Problem Decomposition

Recursion helps break complex problems into smaller, similar subproblems:

Example: Finding Maximum in List
def find_max(numbers):
    # Base case: list with one element
    if len(numbers) == 1:
        return numbers[0]
    
    # Recursive case: compare first element with max of rest
    max_of_rest = find_max(numbers[1:])
    return numbers[0] if numbers[0] > max_of_rest else max_of_rest

numbers = [3, 7, 2, 9, 1, 5]
print(find_max(numbers))  # Output: 9

Decomposition Strategy

When decomposing problems recursively:

  • Identify the smallest case (base case)
  • Figure out how to reduce the problem size
  • Combine the solution to the smaller problem with the current step
  • Ensure each recursive call gets closer to the base case

When to Use Recursion

Recursion is great for:

Tree Structures

Navigating file systems, parsing HTML/XML, traversing directory trees

Mathematical Problems

Factorials, Fibonacci, mathematical sequences, recursive formulas

Divide and Conquer

Sorting algorithms (merge sort, quick sort), binary search

Backtracking

Solving puzzles (Sudoku, N-queens), pathfinding algorithms

When NOT to Use Recursion

Recursion can be inefficient for simple problems that loops handle better. Use recursion when:

  • The problem naturally has a recursive structure (trees, nested data)
  • The recursive solution is clearer than an iterative one
  • Performance isn't critical (recursion can be slower due to function call overhead)

Practice: Recursion

Try It Yourself

Try writing a recursive function:

Press Run to see output

What happened? The function calls itself with a smaller value each time until it reaches the base case (n <= 0). Notice how each recursive call gets closer to the base case!

Summary

In this lesson, you learned:

  • Recursion: Functions that call themselves
  • Base case: Stopping condition to prevent infinite recursion
  • Recursive case: Calling the function with a smaller problem
  • Problem decomposition: Breaking problems into smaller subproblems
  • When to use: Tree structures, mathematical problems, divide-and-conquer algorithms

Remember

Recursion is a powerful tool, but always ensure you have a base case! Start with simple recursive problems and work your way up to more complex ones.

End-of-Lesson Exercises

Think about these questions to reinforce what you've learned:

Exercise 1: Recursion Components

What are the two essential components of a recursive function? Why is each important?

Exercise 2: Problem Decomposition

How does recursion help with problem decomposition? Give an example.