3. Practice Problems on Arrays
Let's look at a couple of recursion examples on arrays. These should give you a strong foundational understanding of recursion, including writing base cases and identifying patterns to call the recursive function to solve subproblems.
All iterative problems (that can be solved using loops) can also be solved using recursion. Identifying the recursive nature of a problem is crucial, and in this section, we'll practice that through a few examples.
Note: The input arrays for the problems in this section are assumed to be valid, i.e., arrays of numbers. Invalid inputs like [null, undefined, "string"]
or non-array values may result in errors.
1. Sum of Array Elements
Let's start with a simple example: calculating the sum of all elements in an array.
Problem statement
arr = [10, 15, 20, 30]
Calculate the sum of all the elements of arr
Iterative approach
const sumOfAll = (arr) => {
let sum = 0;
for (let i = 0; i < arr.length; i++) {
sum += arr[i];
}
return sum;
};
console.log(sumOfAll(arr)); // 75
Time complexity - O(n)
as we iterate through all n elements
Space complexity - O(1)
as we don't use extra memory
Recursive approach
With new Array
const sumOfAll = (arr) => {
if (arr.length === 0) {
return 0;
}
const newArr = arr.slice(1); // arr.slice creates a new array from defined index until end of the array
// Note: Instead of doing arr[0] and then arr.slice(1), we can do arr.shift() as
// shown in the example of 'Array Reverse' problem below.
return arr[0] + sumOfAll(newArr);
};
console.log(sumOfAll(arr));
Time complexity - O(n²)
O(n)
as the recursive function is called n times (for n elements). Each time inside the recursive function, the slicing happens which is againO(n)
so it isO(n*n)
which isO(n²)
overall.
Space complexity - O(n²)
-
O(n)
-> for recursive stack space -
O(n)
-> to form a new array in each recursive call.So overall, it is
O(n²)
Without new Array
const recursiveSum = (arr, index) => {
if (index === arr.length) {
return 0;
}
return arr[index] + recursiveSum(arr, index + 1);
};
console.log(recursiveSum(arr, 0));
Time complexity - O(n)
-
There are
n
elements in the array, and each recursive call processes one element. -
Since we only do one recursive call at a time, the total number of calls is
n
. -
So, time complexity is
O(n)
.
Space complexity - O(n)
-
The recursion depth reaches
n
(each function call is stored in the call stack). -
Since each call has its own stack frame, we use
O(n)
auxiliary space. -
So, space complexity is
O(n)
.
🔥 Challenge
Try to find product of all elements of array using iterative and recursive approaches.
2. Find Maximum Element in Array
Problem statement
arr = [10, -20, 300, 40]
Find the maximum element of the arr
Iterative approach
const findMax = (arr) => {
let maxEl = -Infinity;
for (const ch of arr) {
maxEl = Math.max(maxEl, ch);
}
return maxEl;
};
console.log(findMax(arr)); // 300
Time Complexity - O(n)
O(n)
→ Since we iterate through the array once.
Space Complexity - O(1)
O(1)
→ We use only a single variable (maxEl), so space usage is constant.
Recursive approach
const findMax = (arr, index = 0) => {
if (arr.length === 0) return null; // Guard condition - Explicit check for empty array
if (index === arr.length) return -Infinity; // Base case for recursion
return Math.max(arr[index], findMax(arr, index + 1));
};
console.log(findMax(arr)); // 300
console.log(findMax([])); // -Infinity (natural behavior for empty lists)
Time complexity - O(n)
-
There are
n
elements in the array, and each recursive call processes one element. -
Since we only do one recursive call at a time, the total number of calls is
n
. -
So, time complexity is
O(n)
.
Space complexity - O(n)
-
The recursion depth reaches
n
(each function call is stored in the call stack). -
Since each call has its own stack frame, we use
O(n)
auxiliary space. -
So, space complexity is
O(n)
.
🔥 Challenge
Try to find minimum element in the array using using iterative and recursive approaches.
3. Array Reverse
Problem statement
arr = [10, 20, 30, 40]
Reverse the arr
Iterative approach
for (let i = 0, j = arr.length - 1; i < arr.length / 2; i++, j--) {
// let temp = arr[i];
// arr[i] = arr[j];
// arr[j] = temp;
// The above commented code can also be rewritten using array destructuring assignment in JavaScript:
[arr[i], arr[j]] = [arr[j], arr[i]];
}
console.log(arr); // [ 40, 30, 20, 10 ]
Time complexity - O(n)
as we iterate through all n elements
Space complexity - O(1)
as we don't use extra memory
Recursive approach
with shift operation (Modifying the original array)
const reverseArr = (arr) => {
if (arr.length === 0) {
return arr;
}
const firstElement = arr.shift();
reverseArr(arr);
// Note: The other option here would be to use arr[0] and arr.slice(1)
// as shown in first problem (Sum of array elements) above.
arr.push(firstElement);
return arr;
};
const res = reverseArr(arr);
console.log(res); // [40, 30, 20, 10]
Time complexity - O(n²)
-
arr.shift()
removes the first element of the array. Since arrays are contiguous in memory, removing the first element requires shifting all the remaining elements to the left. This takesO(n)
time in each recursive call. -
Number of recursive calls
O(n)
- The function calls itself recursively untilarr.length === 0
, meaning it makesn
recursive calls. -
Each call takes
O(n)
due to shift(), and there areO(n)
recursive calls. So, the total time complexity isO(n) * O(n) = O(n²)
.
Space complexity - O(n)
- The recursion depth goes up to
n
, leading toO(n)
stack space.
without shift operation - Storing the result in new array
const reverseArr = (arr, index = 0) => {
if (arr.length === 0) {
return [];
}
if (index === arr.length - 1) {
return [arr[index]];
}
const reversedArr = reverseArr(arr, index + 1);
reversedArr.push(arr[index]);
return reversedArr;
};
const res = reverseArr(arr);
console.log(res); // [40, 30, 20, 10]
const res = reverseArr([]); // for an empty array
console.log(res); // []
Time complexity - O(n)
-
There are
n
elements in the array, and each recursive call processes one element. -
Since we only do one recursive call at a time, the total number of calls is
n
. -
So, time complexity is
O(n)
.
Space complexity - O(n)
-
The recursion depth reaches
n
(each function call is stored in the call stack). -
Since each call has its own stack frame, we use
O(n)
auxiliary space. -
So, space complexity is
O(n)
.
4. Array Search (Linear Search)
Problem statement
arr = [1, 2, 3, 2, 2, 4], element = 2
Find the index of the given element.
# output -> should return 1
---
arr = [1, 2, 3, 2, 2, 4], element = 200
Find the index of the given element.
# output -> should return -1 as 200 doesn't exist in `arr`
Iterative approach
const arraySearch = (arr, el) => {
for (let index = 0; index < arr.length; index++) {
if (arr[index] === el) {
return index;
}
}
return -1;
};
const indexOfElement = arraySearch(arr, element);
console.log(indexOfElement); // 1
Time Complexity - O(n)
O(n)
→ Since we iterate through the array once.
Space Complexity - O(1)
O(1)
→ We don't use any extra space here.
Recursive approach
const arraySearch = (arr, index, el) => {
if (index === arr.length) return -1; // Element not found
if (arr[index] === el) {
return index;
}
return arraySearch(arr, index + 1, el);
};
const indexOfElement = arraySearch(arr, 0, element);
console.log(indexOfElement); // 1
Time complexity - O(n)
-
There are
n
elements in the array, and each recursive call processes one element. -
Since we only do one recursive call at a time, the total number of calls is
n
. -
So, time complexity is
O(n)
.
Space complexity - O(n)
-
The recursion depth reaches
n
(each function call is stored in the call stack). -
Since each call has its own stack frame, we use
O(n)
auxiliary space. -
So, space complexity is
O(n)
.
🔥 Challenge
Try to find the index of last occurrence of the element.
[1, 2, 3, 2, 2, 4], element = 2
# output -> Should return index `4`
Hint: The process must start from the last index of the array.
5. Count Occurrences of an Element
Problem statement
arr = [1, 2, 3, 2, 2, 4], element = 2
Find the number of times the given `element` exists in `arr`.
# output -> should return `3`
Iterative approach
const findOccurances = (arr, el) => {
let count = 0;
for (let i = 0; i < arr.length; i++) {
if (arr[i] === el) {
count += 1;
}
}
return count;
};
console.log(findOccurances([1, 2, 3, 2, 2, 4], 2)); // 3
Time Complexity - O(n)
O(n)
→ Since we iterate through the array once.
Space Complexity - O(1)
O(1)
→ Only one count variable is used, which is constant space.
Recursive approach
const findOccurances = (arr, el, index = 0) => {
if (index === arr.length) {
return 0;
}
let count = (arr[index] === el ? 1 : 0) + findOccurances(arr, el, index + 1);
return count;
};
console.log(findOccurances([1, 2, 3, 2, 2, 4], 2)); // 3
This can also be written using normal if-else
condition
const findOccurrences = (arr, el, index = 0) => {
if (index === arr.length) {
return 0;
}
let count = 0;
if (arr[index] === el) {
count = 1;
}
count += findOccurrences(arr, el, index + 1);
return count;
};
console.log(findOccurrences([1, 2, 3, 2, 2, 4], 2)); // 3
Time complexity - O(n)
-
There are
n
elements in the array, and each recursive call processes one element. -
Since we only do one recursive call at a time, the total number of calls is
n
. -
So, time complexity is
O(n)
.
Space complexity - O(n)
-
The recursion depth reaches
n
(each function call is stored in the call stack). -
Since each call has its own stack frame, we use
O(n)
auxiliary space. -
So, space complexity is
O(n)
.
Key Takeaways
-
The above problems follow a similar pattern and are inherently iterative in nature, but we have solved them recursively to demonstrate that iterative problems can also be approached recursively.
-
A key observation is that each recursive call typically processes a single element.
-
While recursion doesn't always require processing just one element per call, it’s a useful starting point when converting iterative solutions to recursive ones.
-
Base conditions usually involve handling an empty array or when the index reaches the array’s length.
Suggested Problems to Solve
👉 Selection Sort has a similar nature to the above problems, making it a great next step for solidifying your understanding of recursion.
👉 Check out Sorting Algorithms using Recursion on arrays.