0

我目前有一个零填充我的二维数组的问题。我想将我的数组中的当前数据传输到一个新数组,该数组是完全相同的数组,但它周围的边框为 0。例子:

|1 2 3|

|4 5 6|

|7 8 9|

应该成为

|0 0 0 0 0|

|0 1 2 3 0|

|0 4 5 6 0|

|0 7 8 9 0|

|0 0 0 0 0|

 int[,] Array = new int[,] { { 1, 2, 3 }, { 3, 4, 5 }, { 6, 7, 8 } };
        
        int[,] ArrayZeroPad = new int[Array.GetLength(0) + 2, Array.GetLength(1) + 2];
        for (int y = 0; y < Array.GetLength(1); y++)
        {

            for (int x = 0; x < ArrayZeroPad.GetLength(0); x++)
            {
                if (y == 0)
                { ArrayZeroPad[y, x] = 0; }
                else if (y == ArrayZeroPad.GetLength(1))
                { ArrayZeroPad[y, x] = 0; }
                else if (x == 0)
                {
                    ArrayZeroPad[y, x] = 0;

                }
                else if (x == ArrayZeroPad.GetLength(0))
                { ArrayZeroPad[y, x] = 0; }
                else ArrayZeroPad[y, x] = Array[y, x];
            }
        }
        for (int y = 0; y < ArrayZeroPad.GetLength(1); y++)
        {
            Console.WriteLine();
            for (int x = 0; x < ArrayZeroPad.GetLength(0); x++)
            { Console.Write(ArrayZeroPad[y, x]); }
            Console.ReadLine();
        }
    }

到目前为止,这就是我所遇到的,但我一直陷入越界错误,有没有人可以通过一些解释为我解决这个问题?

亲切的问候,D。

4

4 回答 4

1

这不是您要问的(我认为完全不同的选择会很有趣)。

这是适用于任何类型、任何大小的数组的无复制版本。如果原始数组非常大(因为它不需要副本),这是合适的。

它使用二维索引器,该索引器为边缘上的项目返回默认值 T(零或空),并为非边缘值使用原始数组(具有索引偏移):

public class ZeroPadArray <T>
{
    private readonly T[,] _initArray;

    public ZeroPadArray(T[,] arrayToPad)
    {
        _initArray = arrayToPad;
    }

    public T this[int i, int j]
    {
        get
        {
            if (i < 0 || i > _initArray.GetLength(0) + 1)
            {
                throw new ArgumentOutOfRangeException(nameof(i),
                    $@"Index {nameof(i)} must be between 0 and the width of the padded array");
            }
            if (j < 0 || j > _initArray.GetLength(1) + 1)
            {
                throw new ArgumentOutOfRangeException(nameof(j),
                    $@"Index {nameof(j)} must be between 0 and the width of the padded array");
            }

            if (i == 0 || j == 0)
            {
                return default(T);
            }

            if (i == _initArray.GetLength(0) + 1)
            {
                return default(T);
            }

            if (j == _initArray.GetLength(1) + 1)
            {
                return default(T);
            }
            //otherwise, just offset into the original array
            return _initArray[i - 1, j - 1];
        }
    }
}

我只是用一些Debug.Assert电话测试了它。测试覆盖率很弱,但可以说“这可能有效”:

int[,] array = new int[,] { { 1, 2, 3 }, { 11, 12, 13 }, { 21, 22, 23 } };
var paddedArray = new ZeroPadArray<int>(array);
Debug.Assert(paddedArray[0, 0] == 0);
Debug.Assert(paddedArray[4,4] == 0);
Debug.Assert(paddedArray[2,3] == 13);

最后,为了好玩,我添加了一个不错的小技巧,让创建这些东西需要更少的输入。当你调用一个方法时,编译器通常能够从方法参数中推断出对象的泛型类型。这不适用于构造函数。这就是为什么您需要指定new ZeroPadArray<int>(array)即使array显然是一个int.

解决这个问题的方法是创建第二个非泛型类,用作创建事物的静态工厂。就像是:

public static class ZeroPadArray
{
    public static ZeroPadArray<T> Create<T>(T[,] arrayToPad)
    {
        return new ZeroPadArray<T>(arrayToPad);
    }
}

现在,而不是输入:

var paddedArray = new ZeroPadArray<int>(array);

你可以输入:

var paddedArray = ZeroPadArray.Create(array);

为您节省两个打字字符(但是,您需要承认打字<int>是令人沮丧的)。

于 2021-09-22T16:05:13.993 回答
0

您似乎混淆了维度 -Array.GetLength(0)用于访问中的第一个,Array[i, j]用于Array.GetLength(1)第二个。您也可以通过仅扫描Array元素并将目标索引调整一个来简化复制,您无需显式设置其他索引以0使其为您完成(除非您正在使用stackalloc跳过本地初始化,但我非常怀疑这是案子):

var length0 = Array.GetLength(0);
var length1 = Array.GetLength(1);
for (int i = 0; i < length0; i++)
{
    for (int j = 0; j < length1; j++)
    {
        ArrayZeroPad[i + 1, j + 1] = Array[i, j];
    }
}

在“打印”方法中也是 -y应该是第一个维度和x- 第二个:

var length = ArrayZeroPad.GetLength(0);
for (int y = 0; y < length; y++)
{
    Console.WriteLine();
    var i = ArrayZeroPad.GetLength(1);
    for (int x = 0; x < i; x++)
    {
        Console.Write(ArrayZeroPad[y, x]);
    }
    Console.ReadLine();
}
于 2021-09-22T14:39:16.477 回答
0
        int[,] Array = new int[,] { { 1, 2, 3 }, { 3, 4, 5 }, { 6, 7, 8 } };
        int[,] ArrayZeroPad = new int[Array.GetLength(0) + 2, Array.GetLength(1) + 2];

        for (int x = 0; x < ArrayZeroPad.GetLength(0); x++)
        {
            for (int y = 0; y < ArrayZeroPad.GetLength(0); y++)
            {
                //First row and last row
                if (x == 0 || x == ArrayZeroPad.GetLength(0) - 1)
                    ArrayZeroPad[x, y] = 0;
                else
                {
                    //Fist column and last column
                    if (y == 0 || y == ArrayZeroPad.GetLength(0) - 1)
                        ArrayZeroPad[x, y] = 0;
                    else
                    {
                        //Content
                        ArrayZeroPad[x, y] = Array[x-1, y-1];
                    }
                }
            }
        }
于 2021-09-22T14:46:48.330 回答
0

您也可以使用Array.Copy(). 如果您需要最高性能并且数组足够大,那么这可能比显式复制每个元素要快:

public static int[,] Pad(int[,] input)
{
    int h = input.GetLength(0);
    int w = input.GetLength(1);
    var output = new int[h+2, w+2];

    for (int r = 0; r < h; ++r)
    {
        Array.Copy(input, r*w, output, (r+1)*(w+2)+1, w);
    }

    return output;
}

(r+1)*(w+2)+1需要一些解释。Array.Copy()将 2D 数组视为线性 1D 数组,并且您必须将副本的目标偏移量指定为与 1D 数组开头的偏移量(按行优先顺序)。

由于w是输入数组的宽度,并且r是输入数组的当前行,因此当前输入行的副本的目标将是输出行号(r+1)乘以输出行宽度(w+2),再加1上左手0输出数组中的列。

使用Buffer.BlockCopy()(对字节进行操作)可能会更快:

public static int[,] Pad(int[,] input)
{
    int h = input.GetLength(0);
    int w = input.GetLength(1);
    var output = new int[h+2, w+2];

    for (int r = 0; r < h; ++r)
    {
        Buffer.BlockCopy(input, r*w*sizeof(int), output, ((r+1)*(w+2)+1)*sizeof(int), w*sizeof(int));
    }

    return output;
}

与往常一样,只有在性能至关重要时才值得担心,即使这样,也只有在您对代码进行基准测试以验证它实际上更快之后。

于 2021-09-22T15:27:52.267 回答