Resources | Subject Notes | Computer Science
Recursion is a powerful programming technique where a function calls itself within its own definition. It's a way to solve problems by breaking them down into smaller, self-similar subproblems. A recursive function has a base case and a recursive step.
When a recursive function is called, it's added to the call stack. The function continues to call itself, adding new frames to the stack. Each frame contains the function's local variables and the return address. When the base case is reached, the function returns a value. This value is then used by the previous call frame to calculate its own return value, and so on, until the initial call returns a final result.
A classic example of recursion is calculating the factorial of a non-negative integer. The factorial of n (denoted as n!) is the product of all positive integers less than or equal to n. For example, 5! = 5 * 4 * 3 * 2 * 1 = 120.
The factorial function can be defined recursively as follows:
Step | Description | Example (n=3) |
---|---|---|
1 | Check if n is 0. If yes, return 1. | n = 3, n is not 0 |
2 | Otherwise, return n multiplied by the result of calling the factorial function with n-1. | n = 3, return 3 * factorial(2) |
3 | The call factorial(2) will then execute the same process. | factorial(2) returns 2 * factorial(1) |
4 | The call factorial(1) will then execute the same process. | factorial(1) returns 1 * factorial(0) |
5 | The call factorial(0) will hit the base case and return 1. | factorial(0) returns 1 |
6 | The values are then returned back up the call stack. | factorial(1) returns 1 * 1 = 1 |
7 | factorial(2) returns 2 * 1 = 2 | |
8 | factorial(3) returns 3 * 2 = 6 |
Tail recursion is a special form of recursion where the recursive call is the very last operation performed in the function. Some compilers can optimize tail-recursive functions into iterative code, eliminating the function call overhead and preventing stack overflow. However, not all languages or compilers support tail recursion optimization.
The Fibonacci sequence is a classic example often used to illustrate recursion. The sequence is defined as:
Here's a simple recursive implementation:
While recursion can be elegant, iterative solutions (using loops) are often more efficient in terms of memory usage and speed. The choice between recursion and iteration depends on the specific problem and the trade-offs between readability and performance.