简体   繁体   中英

pull all non-zero elements in an array to the left - c#

If I have the following array

{ 1, 0, 0, 1, 2, 0, 1 }

And I want a method that will take the array and change it to

{ 1, 1, 2, 1, 0, 0, 0 }

What would be the best algorithm to do this? Is it possible to do this in O(N) time?

this question is essentially my exact question except in python not c#, in case I was not clear: (only difference is move the zeros to the right, not left) how to move all non-zero elements in a python list or numpy array to one side?

Thanks

EDIT: I've ran into another problem that I didn't consider at first. I'm actually trying to run this algorithm on a 2d array, but only on one particular dimension. how would I change to account for this?

It is possible to do it in O(n) time and O(1) space complexity.
Start with a low pointer at 0 and a high pointer at last index of the array.
Algorithm :
1. Increment low till you find 0, decrement high till you find a non-zero number.
2. Swap Array[low] and Array[high].
3. Repeat steps 1 and 2 till low is less than high.

UPDATED FOR 2D ARRAY

int[,] array = 
{
    { 1, 0, 0, 1, 2, 0, 1 }, // Row 0
    { 1, 0, 0, 1, 2, 0, 1 }, // Row 1
    { 1, 0, 0, 1, 2, 0, 1 }  // Row 2
};

PullNonZerosToLeft(array, 1);

for (int row = 0; row <= array.GetUpperBound(0); row++)
{
    for (int col = 0; col <= array.GetUpperBound(1); col++)
    {
        Console.Write("{0} ", array[row,col]);
    }
    Console.WriteLine();
}

PullNonZerosToLeft()

public static void PullNonZerosToLeft(int[,] array, int row)
{
    if (row > array.GetUpperBound(0))
    {
        return;
    }

    // Used to keep track of the swap point
    int index = 0;
    for (int i = 0; i <= array.GetUpperBound(1); i++)
    {
        if (array[row, i] == 0)
        {
            continue;
        }

        int temp = array[row, i];
        array[row, i] = array[row, index];
        array[row, index] = temp;
        index++;
    }
}

Results:

1 0 0 1 2 0 1
1 2 1 1 0 0 0
1 0 0 1 2 0 1

UPDATED FOR JAGGED ARRAY

A non- Linq approach, where you swap all non-zero elements with zero elements.

int[][] array = 
{
    new[] { 1, 0, 0, 1, 2, 0, 1 }, // Row 0
    new[] { 1, 0, 0, 1, 2, 0, 1 }, // Row 1
    new[] { 1, 0, 0, 1, 2, 0, 1 }  // Row 2
};

PullNonZerosToLeft(array, 1);

foreach (int[] row in array)
{
    Console.WriteLine(String.Join(", ", row));
}

PullNonZerosToLeft()

public static void PullNonZerosToLeft(int[][] array, int row)
{
    if (row >= array.Length)
    {
        return;
    }

    // Used to keep track of the swap point
    int index = 0;
    for (int i = 0; i < array[row].Length; i++)
    {
        if (array[row][i] == 0)
        {
            continue;
        }

        int temp = array[row][i];
        array[row][i] = array[row][index];
        array[row][index] = temp;
        index++;
    }
}

Results:

1, 0, 0, 1, 2, 0, 1
1, 1, 2, 1, 0, 0, 0
1, 0, 0, 1, 2, 0, 1

Here's a way you could do it.

var original = new int[] { 1, 0, 0, 1, 2, 0, 1 };
var nonZeroes = original.Where(x => x != 0); //enumerate once
var numberOfZeroes = original.Count() - nonZeroes.Count(); 
return nonZeroes.Concat(Enumerable.Repeat(0, numberOfZeroes)).ToArray();

You can just order by n == 0 .

var original = new int[] { 1, 0, 0, 1, 2, 0, 1 };
var result = original.OrderBy(n => n == 0).ToArray();

Here's a way to do it in O(n) time by creating a new array. Note that this avoids the penalty of looping through the array a second time to get the count of zeros, as in Dleh's answer.

public static int[] PushNonZeroToLeft(int[] aiArray)
{
    var alNew = new List<int>();
    var iZeroCount = 0;

    foreach (int i in aiArray)
        if (i > 0)
            alNew.Add(i);
        else
            iZeroCount++;

    alNew.AddRange(Enumerable.Repeat(0, iZeroCount));

    return alNew.ToArray();
}

Not sure if O(n) can be achieved if you have to mutate the original array...

Here's an O(n) time, O(1) space algorithm for doing what you want. It will also preserve the order of the non-zero elements.

var array = new []{ 1, 3, 3, 1, 2, 0, 1 };
int j = 0;
for( int i = 0; i < array.Length && j < array.Length; ++i )
{
    if( array[i] != 0 ) continue;

    if( j <= i ) j = i + 1;

    while( j < array.Length && array[j] == 0 ) ++j;

    if( j >= array.Length ) break;

    var t = array[i];
    array[i] = array[j];
    array[j] = t;
}

This is how I'd approach it. Nice and simple. I didn't run performance tests but would expect it to be a little more performant than some of the suggestions that use LINQ, or create new arrays.

int[] array = { 1, 0, 0, 1, 2, 0, 1 };

for (int i = 0; i < array.Length; i++)
{
    if (array[i] == 0)
    {
        int j;

        for (j = i + 1; j < array.Length; j++)
        {
            if (array[j] != 0)
                break;
        }

        if (j < array.Length)
        {
            array[i] = array[j];
            array[j] = 0;
        }
        else break;
    }
}

Yes, this is possible to do in O(N) time.

Basically:

  1. Loop through the array
  2. For every element you find that is non-zero, copy it
    • First element should be copied to position 0, next to position 1, etc. Keep track of how far you get with this position.
  3. When you've looped through it, iterate the position you kept track of through the rest of the array until you reach the end, and keep setting elements to zero as you go.

The algorithm will copy , not move , so the 3rd point above will make sure any elements we copied but did not subsequently overwrite will be zeroed out afterwards.

Here's some example code:

int next = 0;

// copy all non-zero elements to the start
foreach (var element in collection)
    if (element != 0)
        collection[next++] = element;

// fill remainder with zeroes
while (next < collection.Count)
    collection[next++] = 0;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM