Skip to content

Recursion

Recursion in Python is a technique where a function calls itself in order to solve a problem. It is often used for problems that can be broken down into smaller, similar sub-problems. A recursive function generally has two parts:

  1. Base Case: The condition under which the function stops calling itself. This prevents the function from calling itself indefinitely.
  2. Recursive Case: The part where the function calls itself with a new set of arguments, moving closer to the base case.

Basic Structure of a Recursive Function:

python
def recursive_function(parameters):
    if base_case_condition:  # Base case to stop recursion
        return some_value
    else:
        # Recursive case, where the function calls itself
        return recursive_function(modified_parameters)

Example 1: Factorial Function (Classic Example of Recursion)

The factorial of a number n is the product of all positive integers less than or equal to n. The factorial function is commonly defined as:

  • n! = n * (n - 1)!
  • Base case: 0! = 1

Recursive Implementation:

python
def factorial(n):
    # Base case: factorial of 0 is 1
    if n == 0:
        return 1
    else:
        # Recursive case
        return n * factorial(n - 1)

# Example usage
print(factorial(5))  # Output: 120

Explanation:

  • For factorial(5), it calls factorial(4), which calls factorial(3), and so on until factorial(0), where the base case returns 1.
  • Then, the results are multiplied as the recursion "unwinds" to return the final result.

Example 2: Fibonacci Sequence

The Fibonacci sequence is a series of numbers where each number is the sum of the two preceding ones, usually starting with 0 and 1. The sequence looks like this: 0, 1, 1, 2, 3, 5, 8, 13, 21,...

  • Recursive definition: fib(n) = fib(n-1) + fib(n-2)
  • Base cases: fib(0) = 0, fib(1) = 1

Recursive Implementation:

python
def fibonacci(n):
    # Base cases
    if n == 0:
        return 0
    elif n == 1:
        return 1
    else:
        # Recursive case
        return fibonacci(n - 1) + fibonacci(n - 2)

# Example usage
print(fibonacci(6))  # Output: 8

Explanation:

  • For fibonacci(6), it calls fibonacci(5) and fibonacci(4), and each of those calls makes further recursive calls until the base cases are reached.

Example 3: Sum of Numbers in a List

You can also use recursion to calculate the sum of all elements in a list.

Recursive Implementation:

python
def sum_list(arr):
    # Base case: If the list is empty, the sum is 0
    if len(arr) == 0:
        return 0
    else:
        # Recursive case: sum of first element + sum of rest of the list
        return arr[0] + sum_list(arr[1:])

# Example usage
print(sum_list([1, 2, 3, 4, 5]))  # Output: 15

Explanation:

  • The function adds the first element of the list and then calls itself with the rest of the list, gradually reducing the list until it reaches an empty list, at which point it returns 0.

Example 4: Reverse a String

Recursion can also be used to reverse a string by breaking the string into its first character and the rest of the string.

Recursive Implementation:

python
def reverse_string(s):
    # Base case: if the string is empty or has one character, it's already reversed
    if len(s) <= 1:
        return s
    else:
        # Recursive case: reverse the substring and add the first character at the end
        return reverse_string(s[1:]) + s[0]

# Example usage
print(reverse_string("hello"))  # Output: "olleh"

Explanation:

  • The function takes the first character and adds it to the reversed version of the rest of the string, which is achieved by calling the function on the substring s[1:].

Key Concepts in Recursion:

  1. Base Case: Every recursive function must have a base case to terminate the recursion. Without a base case, the function would call itself infinitely, leading to a stack overflow error.
  2. Recursive Case: The part of the function where it calls itself with a modified argument, gradually simplifying the problem until the base case is met.
  3. Stack Overflow: Recursion uses the call stack. If the recursion depth is too deep (i.e., the base case is not reached), it can result in a stack overflow error. Python has a limit on the recursion depth to prevent this, which can be modified using sys.setrecursionlimit() (though it's not recommended unless necessary).

Tail Recursion:

In some languages, tail recursion is optimized by the compiler, meaning that it doesn’t add a new stack frame for each recursive call. However, Python does not optimize tail recursion. You can simulate tail recursion by using a helper function or converting the recursion to an iterative approach when needed for performance.

Example of Tail Recursion (Not optimized in Python):

python
def factorial_tail_recursive(n, accumulator=1):
    if n == 0:
        return accumulator
    else:
        return factorial_tail_recursive(n - 1, n * accumulator)

print(factorial_tail_recursive(5))  # Output: 120

When to Use Recursion:

  • Divide and Conquer Problems: Recursion is very effective in problems where the problem can be divided into smaller sub-problems (e.g., merge sort, quick sort).
  • Tree and Graph Traversal: Recursive functions are ideal for traversing hierarchical structures such as trees and graphs.
  • Backtracking: Recursion is used in problems like puzzles (e.g., Sudoku, N-Queens) and combinatorial problems.

Advantages and Disadvantages of Recursion:

Advantages:

  • Recursion often leads to more concise and elegant code.
  • It simplifies problems that can naturally be divided into smaller sub-problems (like tree or graph traversal).

Disadvantages:

  • Recursive solutions can be less efficient due to repeated calculations (especially in problems like Fibonacci without memoization).
  • Deep recursion can lead to stack overflow errors if not carefully managed.

J2J Institute private limited