Skip to main content

Recursion: Basics

Definition​

Recursion is a programming concept where a function calls itself in its own definition.

Terminology​

  • Base Case: The condition that stops the recursion
factorial(n):
if n <= 0: // base case
return 1
else:
return n * factorial(n-1)
  • Recursive Case: The case where the function calls itself
  • Stack Overflow: If the base case is not reached or defined, a stack overflow error may occur due to the continuous (infinite) addition of functions to the stack
  • Direct Recursion: Function calls itself
  • Indirect Recursion: One function calling another, forming a cycle
  • Tailed Recursion: When the recursive call is the last operation in the function, and its result is directly returned
factorial_tail(n, result=1):
if n <= 1:
return result
else:
return factorial_tail(n-1, n*result)
  • Non-Tailed Recursion: When the recursive call is not the last operation
  • Mutual Recursion: Two or more functions call each other in a cycle
is_even(n):
if n == 0:
return True
else:
return is_odd(n-1)

is_odd(n):
if n == 0:
return False
else:
return is_even(n-1)
  • Tabulation: Tabulation is a bottom-up approach in dynamic programming that involves storing subproblem results in a table and employs an iterative implementation. This method is particularly effective for problems with a substantial number of inputs and is preferred when subproblems do not overlap
  • Memoization: Memoization is a top-down dynamic programming approach that involves caching the results of function calls through a recursive implementation. This method is effective for problems with a relatively small set of inputs, especially when the subproblems exhibit overlapping subproblems

Advantages​

  • Elegant solution to complex problems
  • Simplifies code and improves readability
  • Can lead to efficient solutions

Disadvantages​

  • May lead to infinite recursion if not handled properly
  • Can be less efficient in certain cases compared to iterative solutions

Use Cases​

  • Mathematical problems like factorial, Fibonacci
  • Tree and graph traversals
  • Divide and conquer algorithms
  • Backtracking problems

Algorithmic Steps​

  • Define a base case: Identify the simplest case for which the solution is known
  • Define a recursive case: Break the problem into smaller subproblems and call the function recursively
  • Ensure termination: Make sure the recursive function reaches the base case to avoid infinite loops
  • Combine solutions: Combine the solutions of subproblems to solve the original problem

Memory Allocation​

Memory is allocated on the stack for each function call, with each call having its own copy of local variables. De-allocation occurs when the base case is reached.

Comparison​

MethodAmount of calls to find 5th elementAmount of calls to find 10th elementAmount of calls to find 5th using Memoization/TabulationAmount of calls to find 10th using Memoization/Tabulation
Factorial61136
Fibonacci15177919

Step-by-Step​

  • Mathematical formula: F(n) = F(n-1) + F(n-2)
  • Initial Call: start with the initial call to fibonacci(5)
  • Base Case Check: function checks if the base case is met. In this case, if n is 0 or 1, the Fibonacci number is n. Since 5 is not 0 or 1, we proceed to the recursive case
if n == 0 or n == 1:
return n
  • Recursive Case: since n is 5, we enter the recursive case
else:
return fibonacci(n - 1) + fibonacci(n - 2)
  • First Recursive Call: function makes a recursive call to calculate fibonacci(4)
  • Base Case Check (First Recursive Call): base case is not met for n = 4, so we proceed to the recursive case
else:
return fibonacci(4 - 1) + fibonacci(4 - 2)
  • Second Recursive Call (First Nested): function makes another recursive call to calculate fibonacci(3)
  • Base Case Check (First Nested Recursive Call): once again, the base case is not met for n=3
else:
return fibonacci(3 - 1) + fibonacci(3 - 2)
  • Third Recursive Call (First Nested): another recursive call is made to calculate fibonacci(2)
  • Base Case Check (First Nested Recursive Call): base case is met for n = 2. The function returns 2
if n == 0 or n == 1:
return n
  • First Nested Recursive Return: recursive call fibonacci(2) returns 2
  • Second Nested Recursive Return (First Nested): recursive call fibonacci(3) receives the result (2) and makes another recursive call to fibonacci(1)
  • Base Case Check (Second Nested Recursive Call): base case is met for n = 1. The function returns 1
if n == 0 or n == 1:
return n
  • Second Nested Recursive Return: The recursive call fibonacci(1) returns 1
  • First Recursive Return: recursive call fibonacci(3) receives the results (2 and 1) and returns 3
  • Second Recursive Return: recursive call fibonacci(4) receives the results (3 and 2) and returns 5
  • Final Return: initial call to fibonacci(5) receives the result (5) and returns 5 + 3 = 8
  • Result: final result is fibonacci(5) = 8

Practice​

factorial_recursive(n):
if n == 0 or n == 1:
return 1
return n * factorial_recursive(n - 1)