0

I need to do something like this: Let's say I have an array:

 [3, 4, 1, 2]

I need to swap 3 and 4, and 1 and 2, so my array looks like [4, 3, 2, 1]. Now, I can just do the sort(). Here I need to count how many iterations I need, to change the initial array to the final output. Example:

// I can sort one pair per iteration
let array = [3, 4, 1, 2, 5]
let counter = 0;

//swap 3 and 4
counter++;
// swap 1 and 2
counter++;
// 5 goes to first place
counter++

// now counter = 3 <-- what I need

EDIT: Here is what I tried. doesn't work always tho... it is from this question: Bubble sort algorithm JavaScript

let counter = 0;
    let swapped;
    do {
        swapped = false;
        for (var i = 0; i < array.length - 1; i++) {
            if (array[i] < array[i + 1]) {
                const temp = array[i];
                array[i] = array[i + 1];
                array[i + 1] = temp;
                swapped = true;
                counter++;
            }
        }
    } while (swapped);

EDIT: It is not correct all the time because I can swap places from last to first, for example. Look at the example code above, it is edited now.

Uwe Keim
  • 38,279
  • 56
  • 171
  • 280
Nemanja G
  • 1,572
  • 5
  • 25
  • 43
  • Why not try something? – Mitya Mar 17 '19 at 17:56
  • 2
    Do you need to do that with `.sort` or without `.sort`? – VLAZ Mar 17 '19 at 17:56
  • @VLAZ if I do the sort, i will just sort the array, i need to see how many steps it took to sort that array – Nemanja G Mar 17 '19 at 17:56
  • Well, you need to include what you have tried. Use a [mcve]. – Jerry D Mar 17 '19 at 17:57
  • https://stackoverflow.com/questions/7502489/bubble-sort-algorithm-javascript take a look at this question – ilia Mar 17 '19 at 17:58
  • 2
    This question could use clarification. There are many sort routines--when you say "I need", that suggests that you're looking for the minimal number of sort steps necessary in order to sort the array. If you don't care about the minimal number, a counter inside of `.sort`'s custom comparator func will do to give you the engine's builtin sort function (usually quicksort + insertion sort), but you'd need to keep track of only situations when the comparison results in a swap. – ggorlen Mar 17 '19 at 17:59
  • 1
    @nemanja it actually depends on browser, which algorithm is used to implement the sort method of the array in native code. – ganesh phirke Mar 17 '19 at 17:59
  • To see number of swap, you can refer any sorting algorithm implementation, so you can know number of swap. – ganesh phirke Mar 17 '19 at 18:00
  • @Jake tried it already, not working 100% :/ – Nemanja G Mar 17 '19 at 18:13
  • @Utkanos I edited the question, thats what I tried. – Nemanja G Mar 17 '19 at 18:14
  • @ggorlen I edited the question, maybe its clearer now – Nemanja G Mar 17 '19 at 18:23
  • 1
    Thanks for the update, but it's still not quite clear. It sounds like you're asking for the absolute minimum number of steps needed to sort an array (bubble sort cannot guarantee that, but it's a good attempt). Is that the case? If so, I'd update the title and body to specifically ask that. – ggorlen Mar 17 '19 at 18:30
  • Yes, minimum number of steps to sort the array. I updated the title as well @ggorlen – Nemanja G Mar 17 '19 at 18:33
  • It's still not really clear, unfortunately. You should specify "sorted descending" because that's a non-typical requirement. In your example, you move 5 to the front of the array, but that's not a swap operation, that's a move/shift operation. "How many iterations I need" is also not clear. An iteration is one trip through the entire list, not one swap operation. – ggorlen Mar 17 '19 at 18:48
  • @NemanjaG what you want is bubble sort algorithm, and the question i mentioned is fully about on bubble sort algorithm, maybe you did not understand it – ilia Mar 17 '19 at 18:58
  • This is [cycle sort](https://en.wikipedia.org/wiki/Cycle_sort), `O(n^2)`. – Neil Dec 25 '21 at 17:53

8 Answers8

6

This is most optimal code I have tried so far, also the code is accepted as optimal answer by hackerrank :

function minimumSwaps(arr) {
    var arrLength = arr.length;

    // create two new Arrays 
    // one record value and key separately
    // second to keep visited node count (default set false to all)

    var newArr = [];
    var newArrVisited = [];
    for (let i = 0; i < arrLength; i++) {
        newArr[i]= [];
        newArr[i].value = arr[i];
        newArr[i].key = i;
        newArrVisited[i] = false;
    }

    // sort new array by value

    newArr.sort(function (a, b) {
        return a.value - b.value;
    })

    var swp = 0;
    for (let i = 0; i < arrLength; i++) {

        // check if already visited or swapped
        if (newArr[i].key == i || newArrVisited[i]) {
            continue;
        }

        var cycle = 0;
        var j = i;
        while (!newArrVisited[j]) {

            // mark as visited
            newArrVisited[j] = true;
            j = newArr[j].key; //assign next key
            cycle++;
        }
        if (cycle > 0) {
            swp += (cycle > 1) ? cycle - 1 : cycle;
        } 

    }
    return swp;
}

reference

Vi8L
  • 848
  • 9
  • 11
4
//You are given an unordered array consisting of consecutive integers  [1, 2, 3, ..., n] without any duplicates.
//still not the best

function minimumSwaps(arr) {
     let count = 0;
    for(let i =0; i< arr.length; i++){
        if(arr[i]!=i+1){
            let temp = arr[i];
            arr[arr.indexOf(i+1)] =temp;
            arr[i] = i+1;
            count =count+1;
        }
    }
    return count;
}
Bamidele Alegbe
  • 484
  • 3
  • 6
1

I assume there are two reasons you're wanting to measure how many iterations a sort takes. So I will supply you with some theory (if the mathematics is too dense, don't worry about it), then some practical application.

There are many sort algorithms, some of them have a predicable number of iterations based on the number of items you are sorting, some of them are luck of the draw simply based on the order of the items to be sorted and which item how you select what is called a pivot. So if optimisation is very important to you, then you'll want to select the right algorithm for the purpose of the sort algorithm. Otherwise go for a general purpose algorithm.

Here are most popular sorting algorithms for the purpose of learning, and each of them have least, worst and average running-cases. Heapsort, Radix and binary-sort are worth looking at if this is more than just an theoretical/learning exercise.

Quicksort

Worst Case: Θ(n 2)

Best case: Θ(n lg n)

Average case: Θ(n lg n)

Here is a Quicksort implementation by Charles Stover

Merge sort

Worst case: Θ(n lg n)

Best case: Θ(n lg n)

Average Case: Θ(n lg n)

(note they're all the same)

Here is a merge sort implementation by Alex Kondov

Insertion sort

Worst case: Θ(n2)

Best case: Θ(n)

Average case:Θ(n2)

(Note that its worst and average case are the same, but its best case is the best of any algorithm)

Here is an insertion sort implementation by Kyle Jensen

Selection sort

Worst case: Θ(n2)

Best case: Θ(n2)

Average case: Θ(n2)

(note they're all the same, like a merge sort).

Here is a selection sort algorithm written by @dbdavid updated by myself for ES6

You can quite easily add an iterator variable to any of these examples to count the number of swaps they make, and play around with them to see which algorithms work best in which circumstance.

If there's a very good chance the items will already be well sorted, insertion sort is your best choice. If you have absolutely no idea, of the four basic sorting algorithms quicksort is your best choice.

Robin Card
  • 11
  • 2
1
function minimumSwaps(arr) {

   var counter = 0;

   for (var i = arr.length; i > 0; i--) {
      var minval = Math.min(...arr); console.log("before", arr);
      var minIndex = arr.indexOf(minval);
      if (minval != = arr[0]) {
         var temp = arr[0];
         arr[0] = arr[minIndex];
         arr[minIndex] = temp; console.log("after", arr);
         arr.splice(0, 1);
         counter++;
      }
      else {
         arr.splice(0, 1); console.log("in else case")
      }

   } return counter;
}

This is how I call my swap function:

minimumSwaps([3, 7, 6, 9, 1, 8, 4, 10, 2, 5]);

It works with Selection Sort. Logic is as follows:

  1. Loop through the array length
  2. Find the minimum element in the array and then swap with the First element in the array, if the 0th Index doesn't have the minimum value founded out.
  3. Now remove the first element.
  4. If step 2 is not present, remove the first element(which is the minimum value present already)
  5. increase counter when we swap the values.
  6. Return the counter value after the for Loop.

It works for all values.

However, it fails due to a timeout for values around 50,000.

Tamir Klein
  • 3,397
  • 1
  • 18
  • 33
1

The solution to this problem is not very intuitive to unless you are already somewhat familiar with computer science or real math wiz, but it all comes down to the number of inversions and the resulting cycles

If you are new to computer science I recommend the following resources to supplement this solution:

If we define an inversion as:

arr[i]>arr[j]

where i is the current index and j is the following index --

then if there are no inversions the array is already in order and requires no sorting.

For Example:

[1,2,3,4,5]

So the number of swaps is related to the number of inversions, but not directly because each inversion can lead to a series of swaps (as opposed to a singular swap EX: [3,1,2]).

So if one consider's the following array:

[4,5,2,1,3,6,10,9,7,8]

This array is composed of three cycles.

Cycle One- 4,1,3 (Two Swaps)

Cycle Two- 5,2 (One Swap)

Cycle Three- 6 (0 Swaps)

Cycle Four- 10,9,7,8 (3 Swaps)

Now here's where the CS and Math magic really kicks in: each cycle will only require one pass through to properly sort it, and this is always going to be true.

So another way to say this would be-- the minimum number of swaps to sort any cycle is the number of element in that cycle minus one, or more explicitly:

minimum swaps = (cycle length - 1)

So if we sum the minimum swaps from each cycle, that sum will equal the minimum number of swaps for the original array.

Here is my attempt to explain WHY this algorithm works:

If we consider that any sequential set of numbers is just a section of a number line, then any set starting at zero should be equal to its own index should the set be expressed as a Javascript array. This idea becomes the criteria to programmatically determined if in element is already in the correct position based on its own value.

If the current value is not equal to its own index then the program should detect a cycle start and recording its length. Once the while loop reaches the the original value in the cycle it will add the minimum number of swaps in the cycle to a counter variable.

Anyway here is my code-- it is very verbose but should work:

export const minimumSwaps = (arr) => {

  //This function returns the lowest value
  //from the provided array.
  //If one subtracts this value the from
  //any value in the array it should equal
  //that value's index.
  const shift = (function findLowest(arr){
      let lowest=arr[0];
      arr.forEach((val,i)=>{
          if(val<lowest){
              lowest=val;
          }
      })
      return lowest;
  })(arr);

  //Declare a counter variable
  //to keep track of the swaps.
  let swaps = 0;

  //This function returns an array equal
  //in size to the original array provided. 
  //However, this array is composed of 
  //boolean values with a value of false.
  const visited = (function boolArray(n){
      const arr=[];
      for(let i = 0; i<n;i++){
          arr.push(false);
      }
      return arr;
    })(arr.length);

  //Iterate through each element of the
  //of the provided array.
  arr.forEach((val, i) => {

    //If the current value being assessed minus
    //the lowest value in the original array
    //is not equal to the current loop index,
    //or, if the corresponding index in
    //the visited array is equal to true,
    //then the value is already sorted 
    if (val - shift === i || visited[i]) return;

    //Declare a counter variable to record
    //cycle length.
    let cycleLength = 0;

    //Declare a variable for to use for the 
    //while loop below, one should start with
    //the current loop index
    let x = i;

    //While the corresponding value in the
    //corresponding index in the visited array
    //is equal to false, then we 
    while (!visited[x]) {

      //Set the value of the current
      //corresponding index to true
      visited[x] = true;

      //Reset the x iteration variable to
      //the next potential value in the cycle  
      x = arr[x] - shift;

      //Add one to the cycle length variable
      cycleLength++;
    };

    //Add the minimum number of swaps to
    //the swaps counter variable, which
    //is equal to the cycle length minus one
    swaps += cycleLength - 1;

  });

  return swaps

}
Mark Heard
  • 11
  • 3
0

This solution is simple and fast.

function minimumSwaps(arr) {

    let minSwaps = 0;

    for (let i = 0; i < arr.length; i++) {
        // at this position what is the right number to be here
        // for example at position 0 should be 1
        // add 1 to i if array starts with 1 (1->n)
        const right = i+1;
        // is current position does not have the right number
        if (arr[i] !== right) {
            // find the index of the right number in the array
            // only look from the current position up passing i to indexOf
            const rightIdx = arr.indexOf(right, i);
            // replace the other position with this position value
            arr[rightIdx] = arr[i];
            // replace this position with the right number
            arr[i] = right;
            // increment the swap count since a swap was done
            ++minSwaps;
        }
    }

    return minSwaps;
}
ecorreia
  • 81
  • 1
  • 1
0

Here is my solution, but it timeouts 3 test cases with very large inputs. With smaller inputs, it works and does not terminate due to timeout.

function minimumSwaps(arr) {
  let swaps = 0;
  for (let i = 0; i < arr.length; i++) {
    if (arr[i] === i + 1) continue;
    arr.splice(i, 1, arr.splice(arr.indexOf(i + 1), 1, arr[i])[0]); //swap 
    swaps++;
  }
  return swaps;
}

I'm learning how to make it more performant, any help is welcome.

Chris Panayotoff
  • 1,485
  • 19
  • 22
0

This is my solution to the Main Swaps 2 problem in JavaScript. It passed all the test cases. I hope someone finds it useful.

//this function calls the mainSwaps function..
function minimumSwaps(arr){
       let swaps = 0;

        for (var i = 0; i < arr.length; i++){
            var current = arr[i];
            var targetIndex = i + 1;
            if (current != targetIndex){
                swaps += mainSwaps(arr, i);
            }
        }

        return swaps;

}

//this function is called by the minimumSwaps function
function mainSwaps(arr, index){
    let swapCount = 0;
    let currentElement = arr[index];
    let targetIndex = currentElement - 1;
    let targetElement = arr[currentElement - 1];

    while (currentElement != targetElement){
        //swap the elements
        arr[index] = targetElement;
        arr[currentElement - 1] = currentElement;

        //increase the swapcount 
        swapCount++;

        //store the currentElement, targetElement with their new values..
        currentElement = arr[index];
        targetElement = arr[currentElement - 1];
    }

    return swapCount;
}

var myarray = [2,3,4,1,5];
var result = console.log(minimumSwaps(myarray));
Alex Irabor
  • 383
  • 1
  • 5
  • 15