Parameterized Recursion
In basic recursion, global variables or implicit logic are sometimes used to control the recursion state. Parameterized recursion involves passing all required information as parameters to the recursive function. This approach results in:
- Better control over recursion flow
- Cleaner and more maintainable code
- Independence from external or global state
Now, let's understand parameterized recursion with the help of a few examples.
Example 1: Print Value X, N Times
Problem: Given two numbers X and N, print the value X exactly N times using recursion.
Recursive Approach With Parameters
#include <bits/stdc++.h>using namespace std;void printXNTimes(int x, int n) { if (n == 0) return; // base case cout << x << " "; // print the value printXNTimes(x, n - 1); // recursive call with reduced n}int main(){ int x = 4, n = 5; // Printing X, N number of times printXNTimes(x, n); return 0;}Explanation: Each recursive call prints the value
x once and decreases n by 1 until n reaches zero, which terminates recursion.
Example 2: Print Numbers from 1 to N
Problem: Given a number N, print all the numbers from 1 to N.
To solve this problem, both head and tail recursion work.
Head Recursion (Work Happens After Recursion)
#include <bits/stdc++.h>using namespace std;// Recursive function to print numbers from 1 to Nvoid print1ToN(int n) { if (n == 0) return; print1ToN(n-1); // recursive call before printing cout << n << " "; // print after recursion call}int main(){ int n = 5; // Printing 1 to N print1ToN(n); return 0;}Explanation: Each recursive call decreases the value of
N and calls the function recursively with the updated value until it reaches 0. On the return path, each call prints the value N until the first call made is returned.If we wish to use Tail recursion to print the numbers from 1 to N, then we need to introduce an index parameter
i. Tail Recursion (Work Happens Before Recursion)
#include <bits/stdc++.h>using namespace std;// Recursive function to print numbers from 1 to Nvoid print1ToN(int i, int n) { if (i > n) return; cout << i << " "; // print before recursion call print1ToN(i + 1, n); // recursive call after printing}int main(){ int n = 5; // Printing 1 to N print1ToN(1, n); return 0;}Example 3: Print Numbers from N to 1
Printing the numbers from N to 1 is similar to printing the numbers from 1 to N.
While printing the numbers from 1 to N, head recursion was used (where work happens after the recursion call). This had printed the numbers in the order from 1 to N.
Now, in order to print the numbers from N to 1, tail recursion can be used. In the tail recursion, work (printing the number) happens before the recursion call. This will print the numbers in decreasing order from N to 1.
Printing N to 1 (Using Tail Recursion)
#include <bits/stdc++.h>using namespace std;// Recursive function to print numbers from N to 1void printNto1(int n) { if (n == 0) return; cout << n << " "; // print before recursion call printNto1(n-1); // recursive call after printing}int main(){ int n = 5; // Printing N to 1 printNto1(n); return 0;}Explanation: Each recursive call prints the value of
N and decreases the value of N by 1 and calls recursively the function with the updated value until it reaches 0. This prints the numbers from N to 1. If we wish to use Head recursion to print the numbers from N to 1, then we need to introduce an index parameter
i. Printing N to 1 (Using Head Recursion)
#include <bits/stdc++.h>using namespace std;// Recursive function to print numbers from N to 1void printNto1(int i, int n) { if (i > n) return; printNto1(i+1, n); // recursive call before printing cout << i << " "; // print after recursion call}int main(){ int n = 5; // Printing N to 1 printNto1(1, n); return 0;}Explanation: Each recursive call increases the value of
i by 1 until i becomes greater than n. On the return trip, each recursive call prints the value of i. This prints all the numbers from N to 1 using Head Recursion.
Head Recursion vs. Tail Recursion
| Aspect | Head Recursion | Tail Recursion |
|---|---|---|
| Order of Execution | Recursive calls before performing work | Work performed before recursive calls |
| Loop Conversion | Harder to convert directly into a loop | Easily convertible into a loop |
| Compiler Optimization | Generally harder to optimize | Easier to optimize via tail call elimination |
| Typical Use Cases | Tree traversals, backtracking | Loop-like computations, accumulations |