将数组中的所有非零元素拉到左边 - c#

时间:2015-07-21 18:49:59

标签: c# arrays

如果我有以下数组

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

我想要一个采用数组并将其更改为

的方法
{ 1, 1, 2, 1, 0, 0, 0 }

这样做的最佳算法是什么?是否可以在O(N)时间内完成这项工作?

这个问题基本上是我的确切问题,除了在python而不是c#中,如果我不清楚:(唯一的区别是向右移动零,而不是左边) how to move all non-zero elements in a python list or numpy array to one side?

由于

编辑:我遇到了另一个我最初没有考虑过的问题。我实际上试图在二维数组上运行此算法,但仅限于一个特定维度。我将如何更改为此?

8 个答案:

答案 0 :(得分:2)

可以在O(n)时间和O(1)空间复杂度下进行 从低指针开始,在数组的最后一个索引处有一个高指针 的算法
1.递减低直到找到0,递减高,直到找到非零数字 2.交换数组[低]和数组[高]。
3.重复步骤1和2,直到低于低。

答案 1 :(得分:2)

更新为2D阵列

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++;
    }
}

结果:

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

更新了JAGGED ARRAY

Linq方法,您将所有非零元素与零元素交换。

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++;
    }
}

结果:

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

答案 2 :(得分:1)

这是你可以做到的一种方式。

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();

答案 3 :(得分:0)

您可以按n == 0订购。

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

答案 4 :(得分:0)

这是通过创建新数组在O(n)时间内完成此操作的方法。请注意,这样可以避免在第二次循环遍历数组以获得零的数量,例如在Dleh的回答中。

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();
}

如果你必须改变原始阵列,不确定是否可以实现O(n)...

答案 5 :(得分:0)

这是一个O(n)时间,O(1)空间算法,用于做你想要的。它还将保留非零元素的顺序。

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;
}

答案 6 :(得分:0)

这就是我接近它的方式。很好,很简单。我没有运行性能测试,但希望它比使用LINQ或创建新数组的一些建议更高效。

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;
    }
}

答案 7 :(得分:-1)

是的,这可以在O(N)时间内完成。

基本上:

  1. 循环遍历数组
  2. 对于您找到的非零的每个元素,请复制它
    • 应将第一个元素复制到位置0,位置1旁边等。跟踪你在这个位置的距离。
  3. 当你循环通过它时,迭代你通过数组其余部分跟踪的位置,直到你到达终点,并在你去的时候将元素设置为零。
  4. 算法将复制,而不是移动,因此上面的第3点将确保我们复制但未随后覆盖的任何元素将在之后归零。

    以下是一些示例代码:

    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;