Published by Patrick Mutisya · 8 days ago
Show understanding of recursion, its structure, execution model and how it is applied in algorithm design.
Recursion is a programming technique where a method or function calls itself directly or indirectly in order to solve a problem. The problem is divided into smaller sub‑problems of the same type until a simple case – the base case – can be solved directly.
Each time a recursive method is invoked, a new activation record (stack frame) is placed on the call stack. When a method returns, its frame is removed, and control returns to the caller.
The factorial of a non‑negative integer \$n\$ is defined as:
\$\$ n! =
\begin{cases}
1 & \text{if } n = 0 \\
n \times (n-1)! & \text{if } n > 0
\end{cases} \$\$
Java implementation:
public static long factorial(int n) {
if (n == 0) { // base case
return 1;
} else { // recursive case
return n * factorial(n-1);
}
}
The \$n^{\text{th}}\$ Fibonacci number \$F_n\$ is defined by:
\$\$ F_n =
\begin{cases}
0 & \text{if } n = 0 \\
1 & \text{if } n = 1 \\
F{n-1} + F{n-2} & \text{if } n \ge 2
\end{cases} \$\$
Simple recursive Java method (inefficient for large \$n\$):
public static long fib(int n) {
if (n == 0) return 0; // base case 1
if (n == 1) return 1; // base case 2
return fib(n-1) + fib(n-2); // recursive case
}
Searches a sorted array \$A\$ for a key \$k\$ between indices \$low\$ and \$high\$.
public static int binarySearch(int[] A, int key, int low, int high) {
if (low > high) {
return -1; // base case: not found
}
int mid = low + (high - low) / 2;
if (A[mid] == key) {
return mid; // base case: found
} else if (key < A[mid]) {
return binarySearch(A, key, low, mid-1); // left half
} else {
return binarySearch(A, key, mid+1, high); // right half
}
}
A recursive call is in tail position if it is the last operation performed by the method. Compilers or interpreters can optimise tail recursion into iteration, eliminating additional stack frames.
Example of tail‑recursive factorial (using an accumulator):
public static long factorialTail(int n) {
return factorialHelper(n, 1);
}
private static long factorialHelper(int n, long acc) {
if (n == 0) {
return acc; // base case
}
return factorialHelper(n-1, n * acc); // tail call
}
| Aspect | Recursion | Iteration |
|---|---|---|
| Code clarity for divide‑and‑conquer problems | Usually clearer and closer to mathematical definition | Often more verbose; requires explicit stack management |
| Memory usage | Uses call stack; risk of overflow for deep recursion | Uses constant stack space; may need auxiliary data structures |
| Performance overhead | Function call overhead each level | Loop control overhead is minimal |
| Tail‑call optimisation | Possible if language/runtime supports it | Not applicable |
| Ease of proving correctness | Inductive proofs align with recursive definition | Often requires loop invariants |
Recursion is a powerful tool for solving problems that can be expressed in terms of smaller instances of the same problem. Mastery involves recognising suitable problems, writing correct base and recursive cases, understanding the call stack, and being aware of performance implications. With practice, students can choose between recursive and iterative solutions appropriately for the Cambridge A‑Level Computer Science exam.