Interview Kickstart has enabled over 21000 engineers to uplevel.
Recursion and Iteration are the basic ways to repeatedly execute a given set of instructions in any programming language, and hence, every software engineer must be familiar with these topics. Many software engineering interview problems can be tackled by the simple use of recursion.
Before we get into the formal definitions, let’s try and understand these concepts through a simple example.
Recursion is essentially a function that calls itself.
Have you watched Christopher Nolan’s Inception? If not, here’s an overview: There are three people — Leo, Tom, and Murphy.
Leo has a machine that lets him enter a target’s dream and can steal information or clues. Leo enters Tom’s dream and realizes the information he needs is not there. Now, while Leo is inside Tom’s dream, he uses his machine to enter Murphy’s dream; a dream within a dream! That's the idea behind recursion.
Note: It’s guaranteed that we either find a set of people or a clue whenever we enter a dream.
This way, we never go into infinite recursion.
Iteration is also a way to approach such a scenario in computer programming. The above problem can be solved by iteration too. In this case, we need to know the people who could or could not have the information. Then, we enter each person’s dream and try to find a clue.
In conclusion, recursion and iteration are different ways to execute a set of instructions repeatedly.
Sometimes we encounter a problem that is too complex to solve directly. In most cases, we try to break such problems down into smaller pieces. Then, we can find a way to solve these smaller pieces and then build up a solution to the entire problem. This is the entire idea behind recursion.
Recursion is when a function calls itself directly or indirectly, and the function calling itself is called a recursive function. It is mainly used when the solution to a bigger problem can be expressed in terms of smaller problems.
To terminate the recursion, we use some conditions so that the function knows when not to make any further recursive call to itself and return; otherwise, it will lead to infinite recursion once the function is called. Such conditions are called base conditions in recursion.
For example, let's define steps for Leo in our example above to get the clue he needs:
We can see the recursive solution for finding the clue can be written in three steps. The base case here is if we have already found the clue, then we simply stop the recursion. Otherwise, we enter some other target’s dream and redo the entire algorithm.
Wondering how many times the function can call itself?
The local variables and parameters being passed by value are created anew each time the function calls itself. They are stored in stack memory whenever a recursive call is made and are deallocated when the execution of the current function call is complete. The state of the functions is also stored in stack memory. Thus, each recursive call consumes some amount of memory on the stack. In case of infinite recursion or when recursion depth is large enough to exhaust the memory on the stack, it will cause a stack overflow error.
Iteration is when we execute a set of instructions repeatedly until the condition controlling the loop becomes false. It includes initialization, comparison, executing statements inside the iteration, and updating the control variable.
In iteration, it is necessary to have the right controlling condition. Otherwise, the program may go into an infinite loop.
For example, let’s write an iterative program to help Leo find his clue:
Here, the while statement in Step 2 is the controlling statement, which will decide when the loop will end.
Recursion and iteration are both different ways to execute a set of instructions repeatedly. The main difference between these two is that in recursion, we use function calls to execute the statements repeatedly inside the function body, while in iteration, we use loops like “for” and “while” to do the same.
Iteration is faster and more space-efficient than recursion. So why do we even need recursion? The reason is simple — it's easier to code a recursive approach for a given problem. Try doing in order tree traversal using recursion and iteration both.
Let’s look at a simple factorial program implemented using recursion and iteration.
For Factorial we can write it as the below formula :
Factorial ( n ) = n * Factorial ( n-1 ) , for all n>=1 ,
Factorial ( 0 ) = 1 , Base Case
So we can write this in a recursive way where if we reach n = 0, then that’s our base case; else, we make the recursive call for n-1.
Explanation:
#include<bits/stdc++.h>
using namespace std;
// Recursive function to find factorial of given number.
int fact(int n) {
// Base condition
if (n == 0) {
return 1;
}
// Recursive call
return n * fact(n-1);
}
int main() {
int n = 5;
cout << n << " factorial = " << fact(n);
return 0;
}
Output:
5 factorial = 120
Iteration can be simply written by using the formula for factorial:
Factorial ( n ) = 1 * 2 * 3 * … * (n-2) * ( n-1 ) * n
Explanation:
#include<bits/stdc++.h>
using namespace std;
// Factorial of given number using iteration.
int factorial(int n) {
int answer = 1;
// initialization; termination condition; control variable update;
for(int i = 2; i <= n; i++) {
answer *= i;
}
return answer;
}
int main() {
int n = 5;
cout << n << " factorial = " << factorial(n);
return 0;
}
Output:
5 factorial = 120
Strengths:
Weaknesses:
Strengths:
Weaknesses:
Here are a few examples of the kind of problems you can expect on recursion and iteration during tech interviews
Let us first look at the overlapping subproblems:
We can see that fib(3) is called two times, fib(2) is called three times, and so on. If we had stored the value of fib(2), fib(3), we would have directly used it instead of computing it again. Imagine calling fib(n) function for n = 100 and the number of function calls we can prevent by just storing the value when we first calculate it.
To do this, we can use an array say ‘mem’ of size n such that mem[i] = -1 if the value of fib(i) hasn’t been calculated yet, else mem[i] = fib(i). After this, whenever we need to calculate fib(i), we directly pick its value from the ‘mem’ array.
Time complexity before memoization: O(2^n)
Time complexity after memoization: O(n)
Space complexity before and after memoization: O(n)
Do the inorder traversal of the given tree and store the result in an array. If the array obtained is sorted, the given tree is a BST; else, it is not a BST.
Time complexity: O(n), n is the number of nodes in the given binary tree.
Space complexity: O(n)
We can, however, optimize the space complexity by keeping track of the previously visited node. If the current node’s value is less than the value of the previous node, then the given binary tree is not a BST.
While traversing a linked list, if we simply print the current element and move to the next element, we get the linked list in forward order. So rather than printing right away, we can move to the next element until we encounter the list’s end. Once we reach the end of the list, we return to the previous recursion call and print our element. Thus we print all elements in reverse order. Below is the pseudocode for it:
printReverse(head):
If the head is NULL, return.
Call printReverse of head->next.
Print head->value.
Here are some more interview questions on recursion and iteration
Nailing tech interviews at FAANG and Tier-1 tech companies can be challenging even for seasoned software engineers. It requires a deep understanding of data structure and algorithms as well as systems design.
If you’re looking for guidance and help to nail these questions and more, sign up for our free webinar.
With Interview Kickstart you can fast track your interview prep, and nail any job interview. Led by industry experts (from the likes of Google, Facebook, and LinkedIn), our instructors will help you build a strong foundation in the subject, and give you all the tools required to be successful in your career and land your dream job.
You can check out some of the success stories of our alumni who have advanced their careers with the help of Interview Kickstart.
Recursion calls a function within itself, while iteration uses loops to repeat a block of code.
Iteration is usually faster and uses less memory compared to recursion.
Use recursion for problems that are naturally recursive, like tree traversal or factorial calculation.
Yes, recursion typically consumes more memory due to the function call stack.
Yes, any recursive function can be converted to an iterative version, though it may be more complex.
Attend our webinar on
"How to nail your next tech interview" and learn