Dexter
Dexter

Reputation: 129

Given an array of 0 and 1, find minimum no. of swaps to bring all 1s together (only adjacent swaps allowed)

If given an array of 1's and 0's, what's good algorithm to show the minimum number of adjacent swaps needed to group all of the 1's together. The 1's don't need to be grouped at any specific place in the array. They just need to be grouped in whatever place provides for the minimum number of adjacent swaps.

For example, if the array looks like this...

1,0,0,1,1,0,1

...the minimum number of adjacent swaps would be 3, because you'd center on index 4 and do the following swaps:

  1. Swap indices 0 and 1, resulting in: 0,1,0,1,1,0,1

  2. Swap indices 1 and 2, resulting in: 0,0,1,1,1,0,1

  3. Swap indices 5 and 6, resulting in: 0,0,1,1,1,1,0

Anyone have a good algorithm for finding the minimum number of adjacent swaps for any array of 1's and 0's?

Upvotes: 7

Views: 20072

Answers (4)

Raju Muke
Raju Muke

Reputation: 41

Approach : This can be done by finding number of zeroes to the right side of every 1 and add them. In order to sort the array every one always has to perform a swap operation with every zero on its right side.

So the total number of swap operations for a particular 1 in array is the number of zeroes on its right hand side. Find the number of zeroes on right side for every one i.e. the number of swaps and add them all to obtain the total number of swaps.

// Java code to find minimum number of swaps to sort a binary array

class MinimumNumberOfSwapsNeeded { 

    static int findMinSwaps(int arr[], int n) 
    { 
        // Array to store count of zeroes 
        int noOfZeroes[] = new int[n]; 
        int i, count = 0; 

        // Count number of zeroes 
        // on right side of every one. 
        noOfZeroes[n - 1] = 1 - arr[n - 1]; 
        for (i = n - 2; i >= 0; i--)  
        { 
            noOfZeroes[i] = noOfZeroes[i + 1]; 
            if (arr[i] == 0) 
                noOfZeroes[i]++; 
        } 

        // Count total number of swaps by adding number 
        // of zeroes on right side of every one. 
        for (i = 0; i < n; i++)  
        { 
            if (arr[i] == 1) 
                count += noOfZeroes[i]; 
        } 
        return count; 
    }       
    // Driver Code 
    public static void main(String args[]) 
    { 
        int ar[] = { 0, 0, 1, 0, 1, 0, 1, 1 }; 
        System.out.println(findMinSwaps(ar, ar.length)); 
    } 
} 

Upvotes: 0

Arnauld
Arnauld

Reputation: 6110

Here is a simple, but not very clever algorithm that will perform an exhaustive search for any input in the range [0, 255].

  • Input:
    • binary string
  • Output:
    • optimal number of steps
    • number of optimal solutions
    • one detailed example

var transition = [],
    isSolution = [];

function init() {
  var msk = [ 3, 6, 12, 24, 48, 96, 192 ],
      i, j, n, x, cnt, lsb, msb, sz = [];

  for(i = 0; i < 0x100; i++) {
    for(n = cnt = msb = 0, lsb = 8; n < 8; n++) {
      if(i & (1 << n)) {
        cnt++;
        lsb = Math.min(lsb, n);
        msb = Math.max(msb, n);
      }
    }
    sz[i] = msb - lsb;
    isSolution[i] = (sz[i] == cnt - 1);
  }
  for(i = 0; i < 0x100; i++) {
    for(j = 0, transition[i] = []; j < 0x100; j++) {
      x = i ^ j;
      if(msk.indexOf(x) != -1 && (x & i) != x && (x & j) != x && sz[j] <= sz[i]) {
        transition[i].push(j);
      }
    }
  }
}

function solve() {
  var x = parseInt(document.getElementById('bin').value, 2),
      path = [ x ],
      list = [],
      i, min, sol = [], res = [];

  recurse(x, path, list);

  for(i in list) {
    if(min === undefined || list[i].length <= min) {
      min = list[i].length;
      (sol[min] = (sol[min] || [])).push(list[i]);
    }
  }
  console.log('Optimal length: ' + (min - 1) + ' step(s)');
  console.log('Number of optimal solutions: ' + sol[min].length);
  console.log('Example:');

  for(i in sol[min][0]) {
    res.push(('0000000' + sol[min][0][i].toString(2)).substr(-8, 8));
  }
  console.log(res.join(' -> '));
}

function recurse(x, path, list) {
  if(isSolution[x]) {
    list.push(path);
    return;
  }
  for(i in transition[x]) {
    if(path.indexOf(y = transition[x][i]) == -1) {
      recurse(y, path.slice().concat(y), list);
    }
  }
}

init();
<input id="bin" maxlength="8" placeholder="enter binary string">
<button onclick="solve()">solve</button>

Upvotes: -1

Jonathan M
Jonathan M

Reputation: 17451

UPDATED:

The algorithm determines center by just getting an array of all indices of 1's. The center of that array will always hold the center index. Much faster.

oneIndices = array of indices of all 1's in the input
middleOfOnesIndices = round(oneIndices.length/2)-1    // index to the center index
minimumSwaps = 0
foreach index i of oneIndices
    minimumSwaps += aboluteValue(oneIndices[middleOfOneIndices]-oneIndices[i])-absoluteValue(middleOfOneIndices-i);

Here's a fiddle to see it in action:

https://jsfiddle.net/3pmwrk0d/6/

This was a fun one. Thanks for the question.

Upvotes: 8

Rajni Gangwar
Rajni Gangwar

Reputation: 96

Hi, firstly I would like to suggest that the minimum number of adjacent swaps would be 2 for your given example instead of 3. As just swap index 0 with index 2. So 1 swap from left and 1 swap from right.

Here is my way to find minimum of swaps to bring the array in consecutive 1's form -

Step 1 : First find the centre index for maximum number of consecutive 1's Step 2 : Parse the left side of array to swap it and count the number of swap in a efficient manner(Do not swap unnecessarily) Step 3 : Do the same for the right side array Step 4 : Plus the counts of both side.

Please have a look at my java program based on same strategy :

`public class MinimumSwap 
{
//function to find consecutive number index
public static int[] getMaxConsecutiveIndex(List<Integer> array)
{
    int desiredIndex = -1;
    int count = 0;
    int dupDesiredIndex = -1;
    int dupCount = 0;

    int i = 0;
    while(i < array.size())
    {
        if(array.get(i) == 0)
        {
            //pass duplcateIndex value to desiredIndex if count is more
            if(dupCount > count)
            {
                desiredIndex = dupDesiredIndex;
                count = dupCount;
            }
            dupDesiredIndex = -1;
            dupCount = 0;
        }
        else 
        {
            if(dupDesiredIndex == -1) 
            {
                dupDesiredIndex = i;
                dupCount = 1;
            }
            else
            {
                dupCount++;
            }
        }
        i++;
    }
    return new int[]{desiredIndex,count};
}

public static int swapCount(List<Integer> array,int startIndex, int      endIndex, boolean side)
{
    // side == false means 0 at the left
    // side == true means 1 at the left
    System.out.println("startIndex  "+startIndex+"  endIndex  "+endIndex+" side "+side);
    int swapCount = 0; 
    if(side == false)
    {
        while(startIndex <= endIndex)
        {
            if(array.get(endIndex) == 0) // swap from the end only if it is 0
            {
                //check for first 1 from left to swap
                while(array.get(startIndex) == 0 && (startIndex != endIndex))
                    startIndex++;

                if(array.get(startIndex) == 1)  
                {
                    // now swap
                    int temp = array.get(startIndex);
                    array.set(startIndex, array.get(endIndex));
                    array.set(endIndex,temp);
                    swapCount++;
                    endIndex--;
                }
            }
            endIndex--;
        }
    }
    else
    {
        while(startIndex <= endIndex)
        {
            if(array.get(startIndex) == 0) // swap from the starting only if it is 0
            {
                //check for first 1 from right to swap
                while(array.get(endIndex) == 0 && (startIndex != endIndex))
                    endIndex--;
                if(array.get(endIndex) == 1)    
                {
                    // now swap
                    int temp = array.get(startIndex);
                    array.set(startIndex, array.get(endIndex));
                    array.set(endIndex,temp);
                    swapCount++;
                    startIndex++;
                }
            }
            startIndex++;
        }
    }
    return swapCount;
}

public static void main(String...strings)
{
    List<Integer> arr = new ArrayList<Integer>();
    int temp[] = {0,1,1,0,0,0,1,1,1,0,1,1,1,0,1,1,1,1,0,1};
    //int temp[] = {1,0,0,1,1,0,1};
    for(int i=0; i<temp.length; i++)
        arr.add(temp[i]);


    int centerIndex = getMaxConsecutiveIndex(arr)[0];
    int consequtivecount = getMaxConsecutiveIndex(arr)[1];
    System.out.println("centerIndex "+centerIndex+"  consequtivecount "+consequtivecount);
    int swapCountLeft = swapCount(arr,0, centerIndex-1, false);
    int swapCountRight = swapCount(arr,centerIndex+consequtivecount, arr.size()-1, true);
    System.out.println("total swap count "+swapCountLeft+" :: "+swapCountRight);
    System.out.println("array after swapping "+arr);
}

} `

I am not very sure about performance. But as per my knowledge it should not be inefficient. If anyone finds any performance issue please do let me know :)

Upvotes: 0

Related Questions