将2D数组映射到1D数组


90

我想用1D数组表示2D数组。函数将传递两个索引(x,y)和要存储的值。这两个标记将代表一维数组的单个元素,并进行相应设置。我知道一维数组需要具有arrayWidth×arrayHeight的大小,但是我不知道如何设置每个元素。

例如,如何区分(2,4,3)与(4,2,3)?我尝试将数组设置为x * y,但是2 * 4和4 * 2会在数组中产生相同的斑点,我需要它们有所不同。

Answers:


163

您需要确定数组元素是以行顺序还是列顺序存储,然后保持一致。http://en.wikipedia.org/wiki/Row-major_order

C语言对多维数组使用行顺序

要使用一维数组进行模拟,请将行索引乘以宽度,然后按以下方式添加列索引:

 int array[width * height];

 int SetElement(int row, int col, int value)
 {
    array[width * row + col] = value;  
 }

7
我认为这个答案更清晰,特别是对于初学者,最好不要一行编写代码...!无论如何,这都是坏习惯.. :)
Lipis 2010年

3
当您的编译器(例如嵌入式系统)没有适当的多维数组支持时,此答案也很有用
Alex Marshall

1
令人惊讶的是,有多少人可以正确回答同一问题,但只有一个人以易于理解的方式说出了它。这是一个简单的答案。但是,约翰是唯一为此提供好的答案的人。剩下的都是垃圾,只有那些已经知道答案的人才能轻易理解。感谢约翰,他实际上是用英语而不是外语来讲话。只是说明一些人在教学上有多么糟糕,以及像约翰·诺勒这样的优秀老师如何比其他人更有效地简化和交流。
user2948630

6
最好显示如何反转此映射:如果1D数组的索引为alpha,而2D数组N在两个方向上都具有带有索引的维x, y,则根据@JohnKnoeller ,alpha=x+N*y。反过来的方法是设置x=alpha%Ny= (alpha-alpha%N)/N
蒂姆(Tim)

我几乎每天都来这里!
Felipe Gutierrez

23

将2D数组索引重新计算为1D数组索引的典型公式为

index = indexX * arrayWidth + indexY;

或者,您可以使用

index = indexY * arrayHeight + indexX;

(假设arrayWidth沿X轴和arrayHeightY轴测量)

当然,可以提供许多不同的公式来提供替代的唯一映射,但是通常不需要这样做。

在C / C ++语言中,内置多维数组存储在内存中,因此最后一个索引更改最快,这意味着对于声明为

int xy[10][10];

元素xy[5][3]紧随其后xy[5][4]在内存中。您可能还希望遵循该约定,根据您认为是两个索引中的“最后一个”的索引(X或Y)选择以上两个公式之一。


17

示例:我们要表示大小为SIZE_X和SIZE_Y的2D数组。这意味着我们将有MAXY个连续的MAXX大小的行。因此,设定函数为

void set_array( int x, int y, int val ) { array[ x * SIZE_Y + y ] = val; }

得到的将是:

int get_array( int x, int y ) { return array[ x * SIZE_Y + y ]; }

1
MAXXMAXY值是容易混淆的名字命名,因为最大值xyMAXX - 1MAXY - 1分别。也许SIZE_X并且SIZE_Y可能会更好?
caf 2010年

3
[y * maxx + x]是列顺序,而不是行顺序。这是MATLAB的工作方式,但不是阵列的方式通常C.工作
约翰Knoeller

@everyone:除非您一直保持数据接触,而纯粹是这两个get / set函数,并且它们使用相同的公式,否则您可以像THIS或THAT那样进行操作。(保证!)
imacake

宏可能更适合这里,所以你不能堆放在什么应该是一个低级别的数据访问(尤其是在伪2D阵列1D索引不必要的函数调用有时是一种优化技术。
krs013

假设该代码是类成员,则将内联该代码。否则,明确直列是MUCH比宏更好。
Kornel Kisielewicz

7

正如其他人所说的C映射按行顺序

   #include <stdio.h>

   int main(int argc, char **argv) {
   int i, j, k;
   int arr[5][3];
   int *arr2 = (int*)arr;

       for (k=0; k<15; k++) {
          arr2[k] = k;
          printf("arr[%d] = %2d\n", k, arr2[k]);
       }

       for (i=0; i<5; i++) {
         for (j=0; j< 3; j++) {
            printf("arr2[%d][%d] = %2d\n", i, j ,arr[i][j]);
         }
       } 
    } 

输出:

arr[0] =  0
arr[1] =  1
arr[2] =  2
arr[3] =  3
arr[4] =  4
arr[5] =  5
arr[6] =  6
arr[7] =  7
arr[8] =  8
arr[9] =  9
arr[10] = 10
arr[11] = 11
arr[12] = 12
arr[13] = 13
arr[14] = 14
arr2[0][0] =  0
arr2[0][1] =  1
arr2[0][2] =  2
arr2[1][0] =  3
arr2[1][1] =  4
arr2[1][2] =  5
arr2[2][0] =  6
arr2[2][1] =  7
arr2[2][2] =  8
arr2[3][0] =  9
arr2[3][1] = 10
arr2[3][2] = 11
arr2[4][0] = 12
arr2[4][1] = 13
arr2[4][2] = 14

3

使用行主要示例:

A(i,j) = a[i + j*ld]; // where ld is the leading dimension
                      // (commonly same as array dimension in i)

// matrix like notation using preprocessor hack, allows to hide indexing
#define A(i,j) A[(i) + (j)*ld]

double *A = ...;
size_t ld = ...;
A(i,j) = ...;
... = A(j,i);

1

重要的是,以可以使用所用语言检索的方式存储数据。C语言按行优先顺序存储(第一行的所有内容都在前,然后第二行的所有内容……),每个索引从0到其维度1。因此,数组x [2] [3]的顺序为x [0] [0],x [0] [1],x [0] [2],x [1] [0],x [1] [ 1],x [1] [2]。因此,在C语言中,x [i] [j]与一维数组条目x1dim [i * 3 + j]存储在同一位置。如果以这种方式存储数据,则很容易用C语言检索。

Fortran和MATLAB是不同的。它们以列优先顺序存储(第一列的所有内容都排在第一位,然后第二列的所有内容都在此存储...),并且每个索引从1到其维数。因此索引顺序是C的倒数,所有索引都大1。如果按C语言顺序存储数据,则FORTRAN可以使用X_FORTRAN(j + 1,i + 1)查找X_C_language [i] [j]。例如,X_C_language [1] [2]等于X_FORTRAN(3,2)。在一维数组中,该数据值位于X1dim_C_language [2 * Cdim2 + 3],该位置与X1dim_FORTRAN(2 * Fdim1 + 3 +1)相同。请记住,Cdim2 = Fdim1是因为索引的顺序相反。

MATLAB与FORTRAN相同。除了索引通常从1开始,Ada与C相同。任何语言的索引都将以C或FORTRAN顺序之一开始,并且索引将从0或1开始,并且可以相应地进行调整以获取存储的数据。

抱歉,如果这种解释令人困惑,但我认为对于程序员而言,这是正确且重要的。


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.