Data Structures and AlgorithmsUnit 43 min read
Recursion: Base Case, Recursion Tree, Tower of Hanoi and Fibonacci
Unit 4 of BSc CSIT DSA: what recursion is, base case and recursive case, how the call stack works, recursion vs iteration, and classic problems: factorial, Fibonacci, GCD and Tower of Hanoi.
Key points
- Recursion is when a function calls itself to solve a smaller instance of the same problem.
- Every recursive function needs a base case that stops the recursion, and a recursive case that moves towards it.
- Each call is pushed on the call stack, so deep recursion uses O(depth) memory and can overflow the stack.
- Tower of Hanoi with n discs needs 2ⁿ − 1 moves.
- Recursion is elegant for naturally recursive problems (trees, divide and conquer); iteration is usually faster and uses less memory.
What is recursion?
Recursion is a technique in which a function solves a problem by calling itself on a smaller instance of the same problem.
Every recursive function has two parts:
- Base case: a small input whose answer is known directly. It stops the recursion.
- Recursive case: the function calls itself with a smaller input that moves towards the base case.
Without a base case, the function calls itself forever until the program runs out of stack memory (stack overflow).
Example 1: factorial
n! = n × (n − 1)!, with 0! = 1.
long fact(int n) {
if (n == 0) return 1; /* base case */
return n * fact(n - 1); /* recursive case */
}
How the call stack works for fact(3):
- fact(3) waits for fact(2)
- fact(2) waits for fact(1)
- fact(1) waits for fact(0)
- fact(0) returns 1
- fact(1) returns 1 × 1 = 1
- fact(2) returns 2 × 1 = 2
- fact(3) returns 3 × 2 = 6
Each call is pushed onto the call stack and popped when it returns. Time O(n), space O(n).
Example 2: Fibonacci
fib(0) = 0, fib(1) = 1, fib(n) = fib(n − 1) + fib(n − 2).
int fib(int n) {
if (n <= 1) return n;
return fib(n - 1) + fib(n - 2);
}
The recursion tree for fib(5) recomputes fib(3) twice and fib(2) three times. The number of calls grows about as fast as 1.6ⁿ, so the time is exponential. A loop, or storing the results (memoisation), makes it O(n).
Example 3: GCD (Euclid)
gcd(a, b) = a if b = 0; otherwise gcd(b, a mod b).
int gcd(int a, int b) { return b == 0 ? a : gcd(b, a % b); }
gcd(48, 18) = gcd(18, 12) = gcd(12, 6) = gcd(6, 0) = 6.
Example 4: Tower of Hanoi
Move n discs from peg A to peg C using peg B. Only one disc moves at a time, and a larger disc may never sit on a smaller one.
- Move n − 1 discs from A to B (using C).
- Move the largest disc from A to C.
- Move n − 1 discs from B to C (using A).
void toh(int n, char from, char via, char to) {
if (n == 1) { printf("Move disc 1 %c -> %c\n", from, to); return; }
toh(n - 1, from, to, via);
printf("Move disc %d %c -> %c\n", n, from, to);
toh(n - 1, via, from, to);
}
Moves: T(n) = 2T(n − 1) + 1 with T(1) = 1, which gives T(n) = 2ⁿ − 1. For 3 discs that is 7 moves; for 64 discs it would take hundreds of billions of years at one move per second.
Types of recursion
- Direct: a function calls itself.
- Indirect: A calls B, and B calls A.
- Tail recursion: the recursive call is the last operation (like gcd above). A compiler can turn it into a loop.
- Tree recursion: more than one recursive call per invocation (like fib).
Recursion vs iteration
| Recursion | Iteration |
|---|---|
| The function calls itself | Uses loops |
| Needs a base case | Needs a loop condition |
| Uses the call stack: O(depth) memory | Constant extra memory |
| Slower (function-call overhead) | Faster |
| Short, clear code for trees and divide-and-conquer | Better for simple repetition |
| Risk of stack overflow | No such risk |
Exam tip
For Tower of Hanoi, write the three steps, the C function, and the 2ⁿ − 1 result. For 3 discs, list all 7 moves: A→C, A→B, C→B, A→C, B→A, B→C, A→C.
Based on the TU BSc CSIT syllabus for Data Structures and Algorithms (CSC211), unit 4.
Discussion
Loading…