如何在 C# 中将 0 放在多维数组的末尾

How to put the 0s at the end of my multidimensional arrays in C#

如何将 0 放在多维数组的末尾。我的数组大小为 4x5。

这些是我的价值观

4 23 1  0  9
0 6  0  77 9
0 23 0  0  66
0 38 65 2

预期结果:

4  23  1   9   6
77 9   23  66  38
65 2   0   0   0
0  0   0   0   0

我用这段代码试过了。我试图将非零数组移动到一个新的多维数组,然后用零填充其余数组,但我一直出现越界错误。

static void MoveZero(int[,] arrayNum)
{
    int newArrayIndex = 0;  
    int newArrayIndex1 = 0;
    int[,] newarrayNum=new int[4,5];
    for (var outer = 0; outer < 4; outer++)
    {
        if(newArrayIndex1>=6){
                newArrayIndex1=0;
            }

        if(newArrayIndex>=5){
                newArrayIndex=0;
            }

        for (var inner = 0; inner < 5; inner++)
        {
            if(arrayNum[outer,inner]==0){
                continue;
            }

            newarrayNum[newArrayIndex,newArrayIndex1] = arrayNum[outer,inner]; 
            newArrayIndex++;
            
        }
        newArrayIndex1++;
        
    }

    Display(newarrayNum);
}

按照步骤进行

  1. 创建相同的空新数组。
  2. 扫描你的数组。
  3. 如果扫描时遇到 non-zero 值,则将其复制到新数组。
  4. 复制值后立即增加新数组的索引。

例子

给定的初始数组是

int[,] array = new int[,]
{
    { 4, 23, 1,  0,  9},
    { 0, 6,  0,  77, 9},
    { 0, 23, 0,  0,  66 },
    { 0, 38, 65, 2,0 }
};

代码为

int[,] array2 = new int[4, 5];
int i2 = 0;
int j2 = 0;

for (int i = 0; i < 4; i++)
{
    for (int j = 0; j < 5; j++)
    {
        if (array[i, j] != 0)
        {
            array2[i2, j2] = array[i, j];
            j2++;
            if (j2 > 4)
            {
                j2 = 0;
                i2++;
            }
        }
    }
}

评论你的代码

您的总体做法是正确的。从您的代码中可以看出您正在扫描数组,并且每当您遇到 non-zero 值时,您都会将其复制到新数组。因此,您正在执行上面给出的步骤 1、2 和 3。但是你在错误的地方增加了新数组的索引。 newArrayIndex1 在外循环中,newArrayIndex 在内循环中。正确的时机是在将值复制到新数组之后。

因此,不合逻辑的算法构建(未遵循第 4 步)最终导致您犯了一个您已经遇到的错误。

对您的代码的一些观察和评论:

  1. 您(以及阅读您代码的任何人)都受益于简单但具有描述性的变量名称。这也有助于调试代码;特别是在 visually/manually 调试它时。
  2. 您在 MoveZero()
  3. 中多次使用值 45
  4. MoveZero() 不知道输入数组的大小(行数和列数)`,但在您的代码中,假定大小已知
  5. 在原始数组中的每次迭代都会检查新数组中使用的索引。由于新数组的索引与原始数组的索引无关,因此在解释代码中的逻辑时这可能会造成混淆并引起噪音。

关于如何根据这些评论更改代码的建议:

  1. 引入简单的描述性变量名
    • arrayNum --> array。推理:您和您的代码都知道输入数组包含数字 (ints),因为它是 int[,] 类型。此外,MoveZero() 中没有其他类型的数组(即您不需要区分整数数组和字符串数组)。尽可能保持简单。
    • outer --> rowIndex/row。推理:当变量描述数组的维度时,更容易想象你的数组是如何迭代的(即在你的代码中随时检查哪个单元格)。 rowcol 是相关词。
    • inner --> colIndex/col。推理:与 outer.
    • 相同
    • newarrayNum --> newArray。推理:与 arrayNum.
    • 相同
    • newArrayIndex --> newRowIndex/newRow。推理:与 outer.
    • 相同
    • newArrayIndex1 --> newColIndex/newCol。推理:与 outer.
    • 相同
  2. 魔法值创建变量
    • 为什么使用 45?他们的意思是什么?很明显,当我们有您的示例输入数组的上下文/您对具有 4 行和 5 列的输入数组的描述时。但是,对于任何只看到 MoveZero() 没有 的人来说,它是 没有 clear/implicit。 =165=]
    • 定义 int rowCount = 4;int colCount = 5; 并在适当的地方替换 45 的使用。
  3. 不假设数组大小
    • 所有 MoveZero() 对输入数组的了解是它的类型是 int[,]。大小是2x3还是134x896,它不知道;因此,不应做出任何规模假设。 计算输入数组的行数和列数,而不是假设它们是45
    • 对于two-dimensional数组,您可以通过检查第0个数组维度(arrayNum.GetLength(0))的长度来查找行数,通过检查第1个数组的长度来查找列数维度 (arrayNum.GetLength(1)).
    • 上一步中rowCountcolCount的实现应该进行调整。行数和列数不应是静态数字。
  4. 在适当的地方进行有效性检查
    • 验证新数组中使用的索引是有效索引只需要在它们被使用的时候做。新数组的索引仅在填充新数组时使用;即在内部 for 循环中,在检查原始数组中的项目是否等于 0.
    • 之后

实施这些更改后,您的方法现在可能如下所示:

static void MoveZero(int[,] array)
{
    int rows = array.GetLength(0);
    int cols = array.GetLength(1);
    
    int newRow = 0;  
    int newCol = 0;
    
    int[,] newArray = new int[rows, cols];
    
    for (var row = 0; row < rows; row++)
    {
        for (var col = 0; col < cols; col++)
        {
            if (array[row, col] == 0)
            {
                continue;
            }

            if (newCol > cols)
            {
                newCol = 0;
            }

            if (newRow > rows)
            {
                newRow = 0;
            }

            newArray[newRow, newCol] = array[row, col]; 
            newRow++; // updating new row index
        }
        
        newCol++; // updating new col index
    }

    Display(newArray);
}

现在,您可能会注意到几件事:

  1. 每当您在新数组中填充一个单元格时,新行索引 就会递增。 新行索引 的递增意味着您在同一列中向下移动。因此,您正在 逐列 填充新数组,即使您正在 逐行 遍历原始数组。据我了解您的问题,您确实希望 逐行 .
  2. 填充新数组
  3. 每当您移动到原始数组中的下一行时(即紧接在外部 for 循环的新迭代之前),新列索引 都会递增。由于您不知道输入数组中 0 值的位置,因此您不能假设 row/col 索引的更改会在代码中的任何时候同时发生在原始数组和新数组中.

通过在新数组的填充前直接添加一个Console.WriteLine()

Console.WriteLine($"newArray[{newRow}, {newCol}] = array[{row}, {col}] = {array[row, col]}");

newArray[newRow, newCol] = array[row, col]; 

我得到以下控制台输出:

newArray[0, 0] = array[0, 0] = 4
newArray[1, 0] = array[0, 1] = 23
newArray[2, 0] = array[0, 2] = 1
newArray[3, 0] = array[0, 4] = 9
newArray[4, 1] = array[1, 1] = 6
Unhandled exception. System.IndexOutOfRangeException: Index was outside the bounds of the array.

考虑到新行索引的快速递增,我认为这就是产生越界错误的原因。

新实现(包括控制台输出)的示例 fiddle 是 here。希望对您有所帮助r 解决问题。


有关如何修改代码以使其工作的建议

  1. 在使用 newCol 值填充 newArray 后直接更新(即,直接在 newArray[newRow, newCol] = array[row, col]; 之后)。但是:不是无条件地 增加 newCol 值(即 newCol++),而是 将值更新为下一个有效列值 .我们可以通过使用 modulo 运算符来实现这一点,如下所示:
    newCol = (newCol + 1) % cols;
    • 在您的示例中,有效列值为 0--4(总列数为 5)。这意味着当 newCol == 4 并且我们希望更新值时,我们希望设置 newCol = 0 而不是 newCol = 5。在模运算中,newCol + 1 被除数 ,总列数是 模数 。模运算的结果,即除法运算 (newCol + 1) / cols 余数 newCol.
    • 的适当新值
  2. 更新newCol值后直接更新newRow值;但是 如果 newCol == 0 (即仅当我们从最右边的列转到最左边的列时)。
  3. 实施这两个更改后,不再需要检查 newColnewRow 值的有效性;可以删除这些检查。

MoveZero() 的最终实现可能如下所示:

static void MoveZero(int[,] array)
{
    int rows = array.GetLength(0);
    int cols = array.GetLength(1);
    
    int[,] newArray = new int[rows, cols];
    
    int newRow = 0;  
    int newCol = 0;
    
    for (var row = 0; row < rows; row++)
    {
        for (var col = 0; col < cols; col++)
        {
            if (array[row, col] == 0)
            {
                continue;
            }

            newArray[newRow, newCol] = array[row, col];
            
            newCol = (newCol + 1) % cols;
            
            if (newCol == 0)
            {
                newRow++;
            }
        }
    }

    Display(newArray);
}

示例 fiddle here.

我想这就是你想要做的

static void Main(string[] args)
{
    var mat1_1d = new int[] {
         4, 23,  1,  0,  9,
         0,  6,  0, 77,  9,
         0, 23,  0,  0, 66,
         0, 38, 65,  2};

    var mat2_2d = Resize(mat1_1d, 4, 5);
    Display(mat2_2d);
    //4   23    1    0    9
    //0    6    0   77    9
    //0   23    0    0   66
    //0   38   65    2    0
    Console.WriteLine();
    
    var mat3_2d = Pack(mat2_2d);
    Display(mat3_2d);
    // 4   23    1    9    6
    //77    9   23   66   38
    //65    2    0    0    0
    // 0    0    0    0    0
}

具有以下定义的函数 Resize()Pack()Display()

/// <summary>
/// Packs the specified matrix. This moves all zeros to the end
/// of the matrix.
/// </summary>
/// <param name="matrix">The input matrix.</param>
/// <returns>A packed matrix of the same size.</returns>
static T[,] Pack<T>(T[,] matrix)
{
    int rows = matrix.GetLength(0);
    int columns = matrix.GetLength(1);
    // Create a flat array from the matrix for sorting
    T[] flat = new T[matrix.Length];
    Buffer.BlockCopy(matrix, 0, flat, 0, Buffer.ByteLength(matrix));
    // Generate a sequence of keys in ascending order
    var keys = Enumerable.Range(1, matrix.Length).ToArray();
    // Modify key sequence to replace 0's with a large number
    keys = keys.Zip(flat, (num, x) => !x.Equals(0) ? num : matrix.Length+1).ToArray();
    // Sort values based on keys
    Array.Sort(keys, flat);
    // Convert back to 2D matrix
    T[,] result = new T[rows, columns];
    Buffer.BlockCopy(flat, 0, result, 0, Buffer.ByteLength(matrix));
    return result;
}

使用辅助函数从数组生成二维矩阵

/// <summary>
/// Convert an array into a 2D matrix
/// </summary>
/// <param name="array">The input array.</param>
/// <param name="rows">The number rows requested.</param>
/// <param name="columns">The number of columns requested.</param>
/// <returns></returns>
static T[,] Resize<T>(T[] array, int rows, int columns)
{
    int length = Math.Max(array.Length, rows * columns);
    var flat = new T[length];
    Buffer.BlockCopy(array, 0, flat, 0, Buffer.ByteLength(array));
    T[,] result = new T[rows, columns];
    Buffer.BlockCopy(flat, 0, result, 0, Buffer.ByteLength(array));
    return result;
}

使用固定列绘制控制台矩阵

/// <summary>
/// Displays the specified matrix in fixed columns of width 4.
/// </summary>
/// <param name="matrix">The matrix to display.</param>
static void Display<T>(T[,] matrix)
{
    int rows = matrix.GetLength(0);
    int columns = matrix.GetLength(1);

    const int width = 4;
    for (int i = 0; i < rows; i++)
    {
        for (int j = 0; j < columns; j++)
        {
            Console.Write($"{matrix[i, j],width} ");
        }
        Console.WriteLine();
    }
}