Recursion
Recursion Tracing means following the execution flow of recursive calls:
- How functions are called (going down the call stack).
- How results come back (unwinding, going up the call stack).
It helps us see what happens inside memory (stack frames) during recursion.
Direct Recursion
When a function calls itself directly.
int factorial(int n) {
if (n == 0) return 1; // Base case
return n * factorial(n - 1); // Direct recursion
}
Tracing
factorial(4)
→ 4 * factorial(3)
→ 3 * factorial(2)
→ 2 * factorial(1)
→ 1 * factorial(0)
→ return 1 (base case)
return 1
return 2 * 1 = 2
return 3 * 2 = 6
return 4 * 6 = 24
Indirect Recursion
When a function calls another function, and that function eventually calls the first one back.
void B(int n);
void A(int n) {
if (n > 0) {
cout << n << " ";
B(n - 1); // A calls B
}
}
void B(int n) {
if (n > 1) {
cout << n << " ";
A(n / 2); // B calls A
}
}
Tracing
A(5) → prints A:5
B(4) → prints B:4
A(2) → prints A:2
B(1) → prints B:1
A(0) stops
Tail Recursion
When the recursive call is the last statement in the function (nothing to do after recursion returns). Can be optimized by the compiler (Tail Call Optimization).
int tailFactorial(int n, int result = 1) {
if (n == 0) return result; // Base case
return tailFactorial(n - 1, n * result); // Tail recursion
}
Here, no pending operations after the recursive call.
Tracing
tailRec(3) → prints 3
tailRec(2) → prints 2
tailRec(1) → prints 1
tailRec(0) → stops
Head Recursion
When the recursive call happens first, before any other statements. Work happens after recursive call returns.
void headRecursion(int n) {
if (n > 0) {
headRecursion(n - 1); // Recursive call first
cout << n << " "; // Work after return
}
}
Tracing (it is different from other print is done returning time)
headRec(3)
→ headRec(2)
→ headRec(1)
→ headRec(0) (base case, returns)
print 1
print 2
print 3
Tree Recursion
When a function calls itself more than once.
int fib(int n) {
if (n <= 1) return n;
return fib(n - 1) + fib(n - 2); // Two recursive calls
}
Tracing
fib(4)
→ fib(3) + fib(2)
fib(3)
→ fib(2) + fib(1)
fib(2)
→ fib(1) + fib(0)
fib(1) → 1
fib(0) → 0
So fib(2) = 1 + 0 = 1
fib(1) → 1
So fib(3) = 1 + 1 = 2
fib(2) again
→ fib(1) + fib(0)
→ 1 + 0 = 1
So fib(4) = 2 + 1 = 3
Nested Recursion
When a recursive function passes a recursive call as an argument.
int nested(int n) {
if (n > 100) return n - 10;
return nested(nested(n + 11));
}
Tracing
nested(95)
→ nested(nested(106)) // first call argument is another call
nested(106) → returns 96 (since >100, returns 106 - 10)
→ nested(96)
→ nested(nested(107))
nested(107) → returns 97
→ nested(97)
→ nested(nested(108))
nested(108) → returns 98
→ nested(98)
...
eventually reaches nested(101) → returns 91