Data Structures (Recursion)
Data Structures (Recursion)
TOPICS COVERED-
▪ Recursion Basics
▪ Basic Problems on Recursion
▪ Tail Recursion
▪ Explanation of Subset Generation Problem
▪ Explanation of Joesphus Problem
▪ Explanation of permutations of a string
▪ Important Problems to solve
Recursion Basics-
What is Recursion?
The process in which a function calls itself directly or indirectly is called recursion and the
corresponding function is called as recursive function. Using recursive algorithm, certain
problems can be solved quite easily. Examples of such problems are Towers of Hanoi
(TOH), Inorder/Preorder/Postorder Tree Traversals, DFS of Graph, etc.
int fact(int n)
{
if (n < = 1) // base case
return 1;
else
return n*fact(n-1);
}
In the above example, the base case for n < = 1 is defined and a larger value of a number
can be solved by converting to a smaller one till the base case is reached.
Why Stack Overflow error occurs in recursion? If the base case is not reached or not
defined, then the stack overflow problem may arise. Let us take an example to understand
this.
int fact(int n)
{
else
return n*fact(n-1);
}
If fact(10) is called, it will call fact(9), fact(8), fact(7), and so on but the number will never
reach 100. So, the base case is not reached. If the memory is exhausted by these functions
on the stack, it will cause a stack overflow error.
Let us take the example of how recursion works by taking a simple function:
3 2 1 1 2 3
When printFun(3) is called from main(), memory is allocated to printFun(3) and a local
variable test is initialized to 3 and statement 1 to 4 are pushed on the stack as shown in
below diagram. It first prints ‘3’. In statement 2, printFun(2) is called and memory is
allocated to printFun(2) and a local variable test is initialized to 2 and statements 1 to 4
Disadvantage of Recursion: Note that both recursive and iterative programs have the same
problem-solving powers, i.e., every recursive program can be written iteratively and vice
versa. Recursive program has greater space requirements than iterative program as all
functions will remain in the stack until the base case is reached. A recursive program also
has greater time requirements because of function calls and return overhead.
Advantages of Recursion: Recursion provides a clean and simple way to write code. Some
problems are inherently recursive like tree traversals, Tower of Hanoi, etc. For such
problems, it is preferred to write recursive code. We can write such codes also iteratively
with the help of the stack data structure.
Example:
array[] = {1, 2, 3, 4, 5}
X = 3.
Solution: The idea is to compare the first element of the array with X. If the element
matches with X then return True otherwise recur for the remaining part of the array.
return recursiveSearch(arr, l + 1,
r - 1, x);
}
Time Complexity: The above algorithm runs in O(N) time where N is the number of
elements present in the array.
Space Complexity: There is no extra space used however the internal stack takes O(N)
extra space for recursive calls.
Problem 2
Given a string, the task is to write a recursive function to check if the given string is
palindrome or not.
Examples:
Solution: The idea to write the recursive function is simple and similar to the above
problem:
Recursive Function:
return true;
}
Tail Recursion-
Tail Recursion: A recursive function is said to be following Tail Recursion if it invokes itself
at the end of the function. That is, if all of the statements are executed before the function
invokes itself then it is said to be following Tail Recursion.
For Example:
void printN(int N)
if(N==0)
return;
else
cout<<N<<" ";
printN(N-1);
5 4 3 2 1
int fact(int N)
{
if (N == 0)
return 1;
return N*fact(N-1);
}
The above function can be written as a Tail Recursive function. The idea is to use
one more argument and accumulate the factorial value in the second argument.
When N reaches 0, return the accumulated value.
Examples:
Input : set = "abc"
Output : "". "a", "b", "c", "ab", "ac", "bc", "abc"
The idea is to consider two cases for every character. (i) Consider current character
as part of the current subset (ii) Do not consider current character as part of the
current subset.
C++ : -
int n = str.length();
// base case
if (index == n) {
return;
// subset
int main()
{ {
powerSet(str);
if (index == n) }
} {
// subset }
Output:
abc
ab
ac
a
bc
b
c
Let us understand the recursion with an example "abc". Every node in the below
tree represents the string curr.
At root, index = 0.
At next level of tree index = 1
At third level, index = 2
At fourth level index = 3 (becomes equal to string length), so we print the subset.
For example, if n = 5 and k = 2, then the safe position is 3. Firstly, the person at position 2 is
killed, then the person at position 4 is killed, then the person at position 1 is killed. Finally,
the person at position 5 is killed. So the person at position 3 survives.
If n = 7 and k = 3, then the safe position is 4. The persons at positions 3, 6, 2, 7, 5, 1 are
killed in order, and the person at position 4 survives.
When we kill k-th person, n-1 persons are left, but numbering starts from k+1 and goes in
modular way.
So we add (k-1) to the returned position to handle all cases and keep the modulo under n.
Finally, we add 1 to the result.
(josephus(n-1, k) + k) % n
We add k because we shifted the positions by k after the first killing. The problem with the
above solution is that the value of (josephus(n-1, k) + k) can become n and overall solution
can become 0. But positions are from 1 to n. To ensure that, we never get n, we subtract 1
and add 1 later. This is how we get
(josephus(n-1, k) + k - 1) % n + 1
josephus(n, k));
return 0;
} JAVA-
// Java code for Josephus Problem
import java.io.*;
class GFG {
if (n == 1)
return 1;
else
int n = 14;
int k = 2;
josephus(n, k));
}}
Idea: We iterate from first to last index. For every index i, we swap the i-th
character with the first index. This is how we fix characters at the current first
index, then we recursively generate all permutations beginning with fixed
characters (by parent recursive calls). After we have recursively generated all
permutations with the first character fixed, then we move the first character
back to its original position so that we can get the original string back and fix
the next character at first position.
Illustration: We swap 'A' with 'A'. Then we recursively generate all permutations
beginning with A. While returning from the recursive calls, we revert the changes
made by them using the same swap again. So we get the original string "ABC".
Then we swap 'A' with 'B' and generate all permutations beginning with 'B'.
Similarly, we generate all permutations beginning with 'C'
/**
* @param i position 1
* @param j position 2
*/
char temp;
temp = charArray[i] ;
charArray[i] = charArray[j];
charArray[j] = temp;
return String.valueOf(charArray);
Output:
ABC ACB BAC BCA CBA CAB
HIMANSHU KUMAR(LINKEDIN)
https://www.linkedin.com/in/himanshukumarmahuri
CREDITS- INTERNET.
DISCLOSURE- ALL THE DATA AND IMAGES ARE TAKEN FROM GOOGLE AND INTERNET.