【问题标题】:How would I go about reversing a 2D array in C?我将如何在 C 中反转二维数组?
【发布时间】:2020-02-12 05:24:34
【问题描述】:

假设我的数组看起来像这样。行数将始终与此程序中的列数匹配。

[0] [1] [2]
[3] [4] [5]
[6] [7] [8]

我需要把它变成相反的形式:

[2] [1] [0]
[5] [4] [3]
[8] [7] [6]

我知道多维数组只是数组的数组,这有望将我的问题的规模缩短为仅反转一维数组,但我不知道如何将相同的想法应用于二维数组。大小并不总是一个 3 x 3 的数组,但行和列总是相同的数字。

【问题讨论】:

  • 数组中的每个单独的数组是一行还是一列?
  • 不确定您的意思,但有一个包含行和列的二维数组。在帖子中,我尽力将其可视化。我在刚才的帖子中编辑了我的数组格式,以便更清楚地了解它的外观,但它是一个简单的二维数组。
  • 行不相互交互,所以从代码开始反转一维数组。发布后,很容易解释如何使代码适应二维数组。
  • 这叫镜像或翻转,不是倒转

标签: c arrays reverse


【解决方案1】:

试试下面的代码,这里 n 是行数,m 是列数。希望这能解决您的问题。编码愉快!

for(i = 0; i < n; i++) {
    for(j = 0; j < m/2; j++) {
        int temp = arr[i][j];
        arr[i][j] = arr[i][m-j-1];
        arr[i][m-j-1] = temp;
    }
}

【讨论】:

    【解决方案2】:

    如果你正在寻找一个反转二维数组的函数,那么你可以使用这样的函数声明:void reverse_2d_arr(int , int (*)[]);

    在哪里,

    void reverse_2d_arr(int size, int arr[size][size]) {
        int i = 0, j, k, temp;
        while(i < size) {
            j = 0;
            k = size - 1;
            while(j < k) {
                temp = arr[i][j];
                arr[i][j] = arr[i][k];
                arr[i][k] = temp;
                k--;
                j++;
            }
            i++;
        }
    }
    

    并使用 reverse_2d_arr(3, arr); 调用它,其中 arr 是您的二维数组,3 是它的大小。

    【讨论】:

    • 我喜欢使用 VLA,让它处理非方阵并不难。不过,我会使用for 循环,更像:void reverse_2d_arr(int size, int arr[size][size]) { for (int i = 0; i &lt; size; i++) { for (int j = 0, k = size - 1; j &lt; k; j++, k--) { int temp = arr[i][j]; arr[i][j] = arr[i][k]; arr[i][k] = temp; } } }
    • 除了 VLA 对于 C11 以后的编译器支持是可选的。大多数编译器将继续支持。
    【解决方案3】:

    使用标准数组

    从效率的角度来看,在迭代列值时,每次迭代交换两个元素。由于您的数组具有固定宽度,因此从开头和结尾元素开始,交换它们,然后从结尾继续工作到中间,例如

    void rev2d (int (*a)[COLS], int rows, int cols)
    {
        for (int i = 0; i < rows; i++)
            for (int j = 0, k = cols - 1; j < k; j++, k--) {
                int tmp = a[i][j];
                a[i][j] = a[i][k];
                a[i][k] = tmp;
            }
    }
    

    (在..[j] 和..[k] 元素之上,每个内部循环的迭代都会交换)

    或者,如果您想使用 while 循环和指向每行中开始和结束元素的指针来做同样的事情(除了反向迭代行),您可以执行以下操作:

    void rev2dptrs (int (*a)[COLS], int rows, int cols)
    {
        while (rows--) {
            int *beg = *(a + rows), *end = *(a + rows) + cols - 1;
            while (end > beg) {
                int tmp = *beg;
                *beg++ = *end;
                *end-- = tmp;
            }
        }
    }
    

    在每种情况下,例如,如果您有:

    #define ROWS 3
    #define COLS ROWS
    ...
        int a[][COLS] = {{ 0, 1, 2 }, { 3, 4, 5 }, { 6, 7, 8 }};
    

    您的函数调用将是:

        rev2d (a, ROWS, COLS);
    

    或者在第二种情况下:

        rev2dptrs (a, ROWS, COLS);
    

    只是正常反转的一个转折。

    使用可变长度数组

    最初的意图是避免由于C11 Standard - 6.7.6.2 Array declarators(p4) "Variable length arrays are a conditional feature that implementations need not support;" 和C11 Standard - 6.10.8.3 Conditional feature macros __STDC_NO_VLA__ 而导致的VLA示例

    但是,正如 cmets 中所指出的那样,并且由于几乎所有主要编译器都将继续提供 VLA 的实际实用性,您可以通过反转声明并指定 rows 和 cols 来提供更多的灵活性大小拳头,然后将数组作为 VLA 传递。好处是它使您摆脱了恒定的大小。例如,可以重写函数,将数组作为 VLA 传递:

    void rev2dvla (int rows, int cols, int a[rows][cols])
    {
        for (int i = 0; i < rows; i++)
            for (int j = 0, k = cols - 1; j < k; j++, k--) {
                int tmp = a[i][j];
                a[i][j] = a[i][k];
                a[i][k] = tmp;
            }
    }
    

    还有指针:

    void rev2dptrsvla (int rows, int cols, int a[rows][cols])
    {
        while (rows--) {
            int *beg = *(a + rows),
                *end = *(a + rows) + cols - 1;
            while (end > beg) {
                int tmp = *beg;
                *beg++ = *end;
                *end-- = tmp;
            }
        }
    }
    

    在这里,好处是您可以摆脱对每行元素数量的整数常量约束。通过在数组参数之前指定rows 和cols 参数,在将数组int a[rows][cols] 指定为允许VLA 成为完整类型的参数之前,rows 和cols 的值是已知的。

    函数调用将是:

        rev2dvla (rows, COLS, a);
    

    和

        rev2dptrsvla (rows, COLS, a);
    

    如果您了解每种方式以及它们与其他方式的不同之处,那么您就可以控制对二维数组进行排序。如果您还有其他问题,请告诉我。

    将完整示例放在一起,至少对上面的每个函数进行一次练习并添加一个 print2D 函数,您可以执行以下操作:

    #include <stdio.h>
    
    #define COLS 3
    
    void rev2d (int (*a)[COLS], int rows, int cols)
    {
        for (int i = 0; i < rows; i++)
            for (int j = 0, k = cols - 1; j < k; j++, k--) {
                int tmp = a[i][j];
                a[i][j] = a[i][k];
                a[i][k] = tmp;
            }
    }
    
    void rev2dptrs (int (*a)[COLS], int rows, int cols)
    {
        while (rows--) {
            int *beg = *(a + rows),
                *end = *(a + rows) + cols - 1;
            while (end > beg) {
                int tmp = *beg;
                *beg++ = *end;
                *end-- = tmp;
            }
        }
    }
    
    void rev2dvla (int rows, int cols, int a[rows][cols])
    {
        for (int i = 0; i < rows; i++)
            for (int j = 0, k = cols - 1; j < k; j++, k--) {
                int tmp = a[i][j];
                a[i][j] = a[i][k];
                a[i][k] = tmp;
            }
    }
    
    void rev2dptrsvla (int rows, int cols, int a[rows][cols])
    {
        while (rows--) {
            int *beg = *(a + rows),
                *end = *(a + rows) + cols - 1;
            while (end > beg) {
                int tmp = *beg;
                *beg++ = *end;
                *end-- = tmp;
            }
        }
    }
    
    void prn2d (int (*a)[COLS], int rows, int cols)
    {
        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < cols; j++)
                printf (" %2d", a[i][j]);
            putchar ('\n');
        }
    }
    
    int main (void) {
    
        int a[][COLS] = {{ 0, 1, 2 }, { 3, 4, 5 }, { 6, 7, 8 }},
            rows = sizeof *a / sizeof **a;
    
        puts ("original:");
        prn2d (a, rows, COLS);          /* print original */
    
        rev2d (a, rows, COLS);          /* reverse col values using indexes */
        puts ("\nreversed using indexes:");
        prn2d (a, rows, COLS);          /* print reversed array */
    
        rev2dptrs (a, rows, COLS);      /* reverse reversed array to restore original */
        puts ("\nrestore original using pointers:");
        prn2d (a, rows, COLS);          /* print original */
    
        rev2dptrs (a, rows, COLS);      /* reverse col values using pointers */
        puts ("\nreverse again using pointers:");
        prn2d (a, rows, COLS);          /* print reversed array */
    
        rev2dvla (rows, COLS, a);       /* reverse col values restoring original */
        puts ("\nrestore original using VLA w/indexes:");
        prn2d (a, rows, COLS);          /* print original */
    
        rev2dvla (rows, COLS, a);       /* reverse col values using indexes */
        puts ("\nreversed with VLA using indexes:");
        prn2d (a, rows, COLS);          /* print reversed array */
    
        rev2dptrsvla (rows, COLS, a);   /* reverse reversed array to restore original */
        puts ("\nrestore original using VLA w/pointers:");
        prn2d (a, rows, COLS);          /* print original */
    
        rev2dptrsvla (rows, COLS, a);   /* reverse col values using pointers */
        puts ("\nreverse again using VLA w/pointers:");
        prn2d (a, rows, COLS);          /* print reversed array */
    }
    

    使用/输出示例

    $ ./bin/revarr2d
    original:
      0  1  2
      3  4  5
      6  7  8
    
    reversed using indexes:
      2  1  0
      5  4  3
      8  7  6
    
    restore original using pointers:
      0  1  2
      3  4  5
      6  7  8
    
    reverse again using pointers:
      2  1  0
      5  4  3
      8  7  6
    
    restore original using VLA w/indexes:
      0  1  2
      3  4  5
      6  7  8
    
    reversed with VLA using indexes:
      2  1  0
      5  4  3
      8  7  6
    
    restore original using VLA w/pointers:
      0  1  2
      3  4  5
      6  7  8
    
    reverse again using VLA w/pointers:
      2  1  0
      5  4  3
      8  7  6
    

    【讨论】:

    • 如果数组有一个固定的宽度(COLS),为什么你将宽度作为参数(cols)而不是在代码中使用COLS?
    • 如果数组被分配并且参数从调用者传递。 (是的,您可以同时删除 rows 和 cols 参数,在简单的情况下只依赖 #define 值),但只是试图使函数通用。
    • 如果你想要函数通用,那么我建议使用void rev2d (int rows, int cols, int a[rows][cols])。不应影响您编写的函数体。
    • 是的,这会起作用,但它仍然会让我留下a 作为 VLA——我打算避免这种情况。我想我可以把它和那个注释一起添加。
    • 有谁知道一个 C 编译器,它通过定义 __STDC_NO_VLA__ 既声称 C11(或 C18)支持又否认支持 VLA?我认为 MSVC 不太可能声称支持 C11,尽管它可能会在这方面变得更好。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-05-22
    • 1970-01-01
    • 1970-01-01
    • 2016-09-24
    • 2021-03-11
    • 2018-12-15
    • 1970-01-01
    相关资源
    最近更新 更多