Recursion is a programming technique where a function calls itself repeatedly to solve a problem.The recursive calls continue until a specific base condition is met, which stops the recursion.
- A function that calls itself is known as a recursive function
- Each self-call of the function is called a recursive call
#include <iostream>
using namespace std;
void printHello(int n) {
// Base Case
if (n == 0) return;
cout << "Hello" << endl;
printHello(n - 1);
}
int main() {
printHello(5);
return 0;
}
Output
Hello Hello Hello Hello Hello
Explanation:
- Demonstrates a simple recursive function that prints "Hello" multiple times
- The function calls itself with a smaller value of n until the base case n == 0 is reached
- Each recursive call prints "Hello" once, resulting in the message being printed five times
Recursive Function
A function that calls itself is called a recursive function. When a recursive function is called, it executes a set of instructions and then calls itself to execute the same set of instructions with a smaller input. A recursive function should contain,
- Recursive Case: Recursive case is the way in which the recursive call is present in the function.
- Base Condition: The base condition terminates the recursion.
returntype function(parameters) {
// base case
if (base condition) {
return base value;
}
// recursive case
return recursive expression involving function(modified parameters);
}
This structure allows problems to be broken down into simpler versions of themselves, making recursion a powerful tool for solving problems that can be defined in terms of smaller instances.
According to this, from the first example, we can deduce that:
Base Condition
if (n == 0) return;
Recursive Case
printHello(n - 1);
Working
To understand how recursion works internally, it is important to understand the behavior of the call stack during recursive function calls.
Whenever a function calls itself, the current function state is stored in the stack, and new recursive calls continue until the base case is reached.
- The descending phase represents recursive calls going deeper into the function
- The ascending phase begins after reaching the base case, where functions return one by one from the stack
The following example demonstrates both the descending phase (going deeper into recursion) and the ascending phase (returning back from recursion):
#include <iostream>
using namespace std;
void f(int n) {
cout << "F(" << n << ")'s Stack Frame Pushed\n";
if (n > 1) {
f(n - 1);
f(n - 1);
}
cout << "F(" << n << ")'s Stack Frame Removed\n";
}
int main() {
f(3);
return 0;
}
Output
F(3)'s Stack Frame Pushed F(2)'s Stack Frame Pushed F(1)'s Stack Frame Pushed F(1)'s Stack Frame Removed F(1)'s Stack Frame Pushed F(1)'s Stack Frame Removed F(2)'s Stack Frame Removed F(2)'s Stack Fr...
In this program, the function f(int n) prints messages when a recursive call is added to the stack (Stack Frame Pushed) and when it is removed (Stack Frame Removed). This helps visualize how the call stack behaves during recursion.
Descending Phase: The function keeps calling itself with n - 1 while n > 1, causing new stack frames to be pushed onto the call stack and increasing its size.
Ascending Phase: Once the base case is reached, the function starts returning back. Each completed call removes its stack frame, and control moves to the previous function call, showing the stack unwinding process clearly.
For f(3), the recursive calls form a tree-like pattern, and the output clearly shows when each function call starts and ends during recursion.
Illustrations:
This example follows a tree recursion pattern, where each function call makes multiple recursive calls (in this case, two). It’s one of the common forms of recursion.
To explore more patterns like linear recursion, tail recursion, and indirect recursion, check out our detailed article on Types of Recursion.
Memory Management in Recursion
Like other functions, the data of a recursive function is stored in stack memory as a stack frame. This stack frame is removed once the function returns a value. In recursion,
- The function call occurs before returning a value, so the stack frames for each recursive call are placed on top of the existing stack frames in memory.
- Once the topmost function returns a value, its stack frame is removed, and control is passed back to the function just before it, resuming execution after the point where the recursive call was made.
- The compiler uses an instruction pointer to keep track of the location to return to after the function execution is complete.
- Unlike iteration, recursion relies on the call stack, making memory management a key differentiator between the two.
Refer these article to know more about Function Call Stack, Difference between recursion and iteration
Stack Overflow
Stack Overflow is an error that occurs when a program uses more stack memory than the system allows, usually because of too many recursive function calls.
It commonly happens when recursion does not stop due to a missing or incorrect base condition.
- Each recursive call creates a new stack frame and uses additional stack memory
- Infinite recursion or excessive recursive calls can exhaust stack memory and terminate the program
Applications of Recursion
Recursion is widely used in computer science and programming. Common applications include:
- Problem Solving: Many problems are inherently recursive like Tower of Hanoi. Recursion also works as a foundation for Divide and Conquer, Backtracking and Dynamic Programming.
- Searching and Sorting: Algorithms like binary search, Merge Sort and Quick Sort are recursive in nature.
- Tree and Graph Traversal: Used in traversals such as depth-first search.
Overall, recursion is a powerful and versatile technique for solving a wide range of problems.
Limitations of Recursion
Recursion is a powerful programming technique, but it also has some limitations that can affect performance and memory usage in certain situations.
- Performance: Can be less efficient than iteration, especially for large data or deep recursion.
- Memory Usage: Each recursive call creates a new stack frame, increasing memory usage and potentially becoming significant for deep recursion.
- Code Complexity: Recursive solutions can be harder to understand than iterative ones.
- Debugging: More difficult to debug, particularly with deep or multiple recursive calls.
- Stack Overflow: Excessive recursion depth can cause a stack overflow, leading to program crashes.