Minimum Swap 2- Hacker Rank -Javascript Solution
By: Tawanda Kanyangarara
Tags: Hackerrank, Algorithms, JavaScript, Arrays, Minimum Swaps

Another algorithm, another article, no interview this time, just Hacker Rank.

Since my last article, I have been hammering through Hacker Rank and Leetcode trying to get my right frame of mind. I promised myself if there was an algorithm that I could not solve without extensive googling, I would write about the solution.

So I ran into this one on the Hacker Ranks interview preparation section under Arrays. As not to retype the internet I will write a tl:dr; version of the problem.

Problem

You are given an unordered array consisting of consecutive integers [1, 2, 3, …, n] without any duplicates. You are allowed to swap any two elements. You need to find the minimum number of swaps required to sort the array in ascending order.

For example, given the array arr= [7, 1, 3, 2, 4, 5, 6] we perform the following steps to solve for the number of steps

i arr.....................swap (indices)

0 [7, 1, 3, 2, 4, 5, 6] swap (0,3) --> Step 1

1 [2, 1, 3, 7, 4, 5, 6] swap (0,1) --> Step 2

2 [1, 2, 3, 7, 4, 5, 6] swap (3,4) --> Step 3

3 [1, 2, 3, 4, 7, 5, 6] swap (4,5) --> Step 4

4 [1, 2, 3, 4, 5, 7, 6] swap (5,6) --> Step 5

5 [1, 2, 3, 4, 5, 6, 7]

It took swaps 5 to sort the array.

Quest for knowledge

Since I said I scoured the internet to find out how this works, I came across these two resources that helped.

Even though the final solution came from Alex’s video, I found that I could skip to the end of the video to see how it was all put together because I had already been through the article by Stoychev that describes the solution process of swapping in a non-interview like structure.

Though it ultimately didn’t work for all the test cases (i think they have beefed up the test cases). It did get my head in the right frame of mind of swapping indexes without using the .indexOf() method because of time complexity problems. He states in his article why indexOf is causing the time out.

I had (incorrectly) assumed our algorith is of complexity O(2) but arr.indexOf must be looping through the array so thus it’s O(n^{2}) due to the nested looping. This is likely the source of our perfomance issues.

Though this optimization of converting the indexOf() operation to a standard for loop, I still didn’t have the optimum solution needed to pass all tests.

[..arr].indexOf(i); // can be converted to

// Note: this is happeing inside another loop

// Index is the iterator of the outer loop const position = index + 1;

let indexToSwap;



for (let i = 0; i < arr.length; i++) {

if (arr[i] === position) {

indexToSwap = i;

break;

}

}

Random note about me: At the beginning of my dev career, I could learn by watching these videos but lately, I have found I get a lot more form reading haha pretty obvious. I think this has to do with my experience, now that i have gone through a couple of years of js documentation, I can understand some of the things other devs talk about.

The solution according to Alex

https://www.youtube.com/watch?v=Mk9Fre9_f64&t=1269s

So upon going through the youtube video, I finally understand how to write this out with the giver time complexity constraints.

In order to reduce the number of iterations, we will use a helper function that takes care of figuring out whether the element needs to be swapped and how many times the swap happens. That is where the juice of the code sits. So our submitted code will look like this.

const minimumSwaps = (arr) => { let minSwapCount = 0; for (let i = 0; i < arr.length; i++) {

minSwapCount += swapsCounter(arr, i); }

return minSwapCount; }

Simple. Right? Now let us get to the actual function swapsCounter(arr,I) to do the actual work.

Inside the helper function, we must check if the currentElement needs to be swapped, if so, find its correct position and drop it there. Once that is done we take the element that we replaced - targetElement — and put that at the original position that currentElement was previously. Then add 1 to the counter. Phew!

[6……..7] …becomes… [7……..6] etc

Keep doing this operation until the array is in the correct order, then return the number of swaps counted.

Initially, I swapped the currentElement with the elements next to it, but Alex’s approach swaps it with the element that is the position of the currentElement.

The thing to take note of here is that when we swap currentElement with the next element in the array we write

arr[i] = arr[i+1]

but when we want to change it with the element at its corresponding position,targetElement, we write

arr[i] = arr[currentElement — 1]

**This had me confused, what if the current element is like 45 and the array length is only 7 ie [45,46,47,48,49,50,51] then I get 7 swaps? need to read into this more.

But the sake of hacker rank it works.

Instead of using an if statement to compare we will use a while statement because we want to loop through the array to find the correct number and positions concerning currentElement.

So our helper function looks like this :

const swapsCounter = (arr, i) => {

Set up variables

// setup swap counter let swapsCount = 0, // current item in array currentElement = arr[i], // target it for sway targetElement = arr[currentElement - 1];

Loop

while (curNUm !== targetElement) {

The swap

// set the element at arr[i] to the targetElement number arr[i] = targetElement; // then set the element at the position of the // the position of current number - 1 in the original arr arr[currentElement - 1] = currentElement;

Increment Counter

// this is a swap so increment the counter by 1 swapsCount++;

Setup variables for the next loop

// set the current number to the target currentElement = targetElement; // set the targetElement to the current number targetElement = arr[currentElement - 1];

Return count to minimumSwaps();

return swapsCount

And there you have the helper function that does the swapping and counting for us.

Full Code

/** * Find the minimum number of swaps * @param {Array} arr */ const minimumSwaps = (arr) => { // min swap counter let minSwapCount = 0; // Loop through array for (let i = 0; i < arr.length; i++) { // swapsCounter() returns 1 if there is a swap // add that to the minSwapCount minSwapCount += swapsCounter(arr, i); } // return the accumplated number of swaps return minSwapCount; }

Helper Function

/** * This is a helper function that allows us to check and swap * elements around whilst inside the for..loop upstairs * * @param {Array} arr array * @param {number} i iterator */ const swapsCounter = (arr, i) => { // setup swap counter let swapsCount = 0, // current item in array currentElement = arr[i], // targetElement it for swap targetElement = arr[curNUm - 1]; // keep doing this operation // untill current number is not equal to the targetElement while (currentElement !== targetElement) { // set the element at arr[i] to the targetElement number arr[i] = targetElement; // then set the element at the position of the // the position of current number - 1 in the original arr arr[currentElement - 1] = currentElement; // this is a swap so increment the counter by 1 swapsCount++; // leave array manipulation and set variables // set the current number to the targetElement currentElement = targetElement; // set the targetElement to the current number targetElement = arr[currentElement - 1]; } // return swap count return swapsCount }

Conclusion

Like with most of these, it’s about how you see the problem. Lately, I have been able to stop when I hit a dead-end and restart the solution from a different perspective. This feels like a new setup er power and I am very happy that these hours of algorithm practice are starting to show some results. This one had me stumped because I didn’t know how to swap the indexes from different positions in the area as opposed to swapping elements side-by-side. Thus hampering my vision of the possible solution.

Now that I have this new technique under my belt, its back to hacker rank for more challenges.