Rearranging an Array: Transforming arr[i] into arr[arr[i]]
Table of Content
Introduction
Aspiring software developers and computer science engineering enthusiasts often encounter captivating challenges that put their problem-solving abilities to the test. In this beginner’s guide to data structures and algorithms, we delve into a fascinating problem involving arrays. We will explore a step-by-step solution to rearrange the elements in the input array, leading to a transformation where each element at index i
becomes the value at index arr[i]
.
The Problem
Let’s consider the problem statement:
Given an array containing numbers in the range from 0 to N-1, the task is to rearrange the array such that each number at the index i
in the array becomes the number at the index a[i]
in the same array.
The Solution
Step 1: Create a New Array and Multiply Every Element by N
We begin by traversing the input array and create a new array by multiplying each element by N
. This multiplication ensures that all elements in the new array become greater than or equal to N
. Since the original array’s elements are in the range [0, N-1]
, this step guarantees that no element will be less than N
.
1
2
3
def create_new_array(arr, N):
new_array = [element * N for element in arr]
return new_array
For example, let’s take an initial array [3, 2, 0, 1]
with N=4:
1
2
3
4
5
Initial array: [3, 2, 0, 1]
N = 4
After multiplying by N:
new_array = [12, 8, 0, 4]
Step 2: Adding the Value of a[new_array[i]/N]
to Each Element
If we take the mod of every element of the new_array by N
, it will give 0. We don’t want that. We want new_array[i]
at index i
to be equal to a[a[i]]
. So, we add new_array[i]
with a[new_array[i]/N]
. By doing so, when we take the mod by N
, it will return a[new_array[i]/N]
, and a[new_array[i]/N]
is nothing but a[a[i]]
.
Now, we iterate through each element of the new_array
, and for each element at the index i
, we need to find the value to be added such that the condition new_array[i]
= a[a[i]]
is satisfied.
1
2
3
4
5
def rearrange_array(new_array, arr, N):
for i in range(len(new_array)):
new_index = new_array[i] // N
new_array[i] += arr[new_index]
return new_array
Let’s walk through the process of adding the values for each element:
- At index 0 (
i=0
): We take the value atnew_array[0]
, which is12
. To find the value to be added, we calculate thenew_index
by dividingnew_array[0]
by N, resulting innew_index = 12/4 = 3
. Now,new_index
represents the value ofa[0]
in the original array. We need to find the valuea[new_index]
in the original array and add it tonew_array[0]
. In this case,a[0]
is3
, soa[new_index]
isa[3]
, which is1
. Thus, we add1
tonew_array[0]
, resulting innew_array[0] += 1 = 12 + 1 = 13
.
1
2
3
4
5
6
7
8
new_array = [12, 8, 0, 4]
At index 0 (i=0):
new_array[0] = 12
new_index = 12/4 = 3
a[new_index] = a[3] = 1
new_array[0] += 1 = 12 + 1 = 13
Resulting array: [13, 8, 0, 4]
- At index 1 (
i=1
): We take the value atnew_array[1]
, which is8
. We calculate thenew_index
by dividingnew_array[1]
by N, resulting innew_index = 8/4 = 2
. Now,new_index
represents the value ofa[1]
in the original array. We need to find the valuea[new_index]
in the original array and add it tonew_array[1]
. In this case,a[1]
is2
, soa[new_index]
isa[2]
, which is0
. Thus, we add0
tonew_array[1]
, resulting innew_array[1] += 0 = 8
.
1
2
3
4
5
6
7
8
new_array = [13, 8, 0, 4]
At index 1 (i=1):
new_array[1] = 8
new_index = 8/4 = 2
a[new_index] = a[2] = 0
new_array[1] += 0 = 8
Resulting array: [13, 8, 0, 4]
- At index 2 (
i=2
): We take the value atnew_array[2]
, which is0
. We calculate thenew_index
by dividingnew_array[2]
by N, resulting innew_index = 0/4 = 0
. Now,new_index
represents the value ofa[2]
in the original array. We need to find the valuea[new_index]
in the original array and add it tonew_array[2]
. In this case,a[2]
is0
, soa[new_index]
isa[0]
, which is3
. Thus, we add3
tonew_array[2]
, resulting innew_array[2] += 3 = 0 + 3 = 3
.
1
2
3
4
5
6
7
8
new_array = [13, 8, 0, 4]
At index 2 (i=2):
new_array[2] = 0
new_index = 0/4 = 0
a[new_index] = a[0] = 3
new_array[2] += 3 = 0 + 3 = 3
Resulting array: [13, 8, 3, 4]
- At index 3 (
i=3
): We take the value atnew_array[3]
, which is4
. We calculate thenew_index
by dividingnew_array[3]
by N, resulting innew_index = 4/4 = 1
. Now,new_index
represents the value ofa[3]
in the original array. We need to find the valuea[new_index]
in the original array and add it tonew_array[3]
. In this case,a[3]
is1
, soa[new_index]
isa[1]
, which is2
. Thus, we add2
tonew_array[3]
, resulting innew_array[3] += 2 = 4 + 2 = 6
.
1
2
3
4
5
6
7
8
new_array = [13, 8, 3, 4]
At index 3 (i=3):
new_array[3] = 4
new_index = 4/4 = 1
a[new_index] = a[1] = 2
new_array[3] += 2 = 4 + 2 = 6
Resulting array: [13, 8, 3, 6]
Step 3: Take the Mod of Each Element by N
In this final step, we complete the rearrangement by taking the modulo of each element in the new_array
by N (4). This step is necessary because we previously multiplied each element by N, and now we need to revert them to their original values.
Let’s take the modulo of each element in new_array
by N (4):
1
2
def take_mod(arr, N):
return [element % N for element in arr]
1
2
After taking the modulo by N:
new_array = [1, 0, 3, 2]
Now the array is rearranged as required, and each element a[i]
is equal to a[a[i]]
.
By mastering this array rearrangement challenge, you will sharpen your problem-solving skills and be better prepared for software developer interviews and similar technical assessments.
Space and Time Complexity Analysis:
Let’s analyze the space and time complexities of the first approach, where we created a new array.
Using a New Array
Space Complexity: In this approach, we created a new array of the same size as the original array. Therefore, the space complexity is O(N)
, where N
is the size of the array.
Time Complexity: The time complexity involves iterating over the array multiple times, which is done in linear time. Thus, the time complexity is O(N)
, where N
is the size of the array. Now, let’s move to the second approach, where we avoid creating a new array by modifying the original array directly.
Modifying the Original Array
Space Complexity: In this approach, we directly modify the original array without creating a new array. Hence, there is no additional space used, and the space complexity is O(1)
, constant space complexity.
Time Complexity: Similar to the first approach, time complexity involves iterating over the array multiple times, which is done in linear time. Therefore, the time complexity is O(N)
, where N
is the size of the array.
For C++ Implementation using Modifying the Original Array:
If you’re interested in exploring the C++ implementation of this magical array rearrangement algorithm, you can find the complete code on our Github repository.
Conclusion
Congratulations on successfully navigating the array rearrangement challenge! Through this beginner’s guide to data structures and algorithms, you’ve gained valuable insights into problem-solving techniques for arrays. The step-by-step solution presented here empowers you to transform an array in a way that each element arr[i]
becomes arr[arr[i]]
.
As you continue your journey in computer science engineering, keep exploring new problems and algorithms. Practicing such challenges will enhance your proficiency as a software developer and strengthen your ability to tackle a wide array of interview questions with confidence.
Remember that continuous learning and practice are the cornerstones of excellence in the world of algorithms and data structures. Happy coding!