就地矩阵旋转

6
我发现了一个有趣的问题,要求将一个NxN矩阵就地旋转90度。我的递归解决方案使用C语言编写,如下所示。然而,当我查找其他解决方案时,大多数都使用嵌套的for循环来完成任务(似乎也可以正常工作)。嵌套循环实现似乎在O(n^2)时间内运行。
参见: 如何旋转二维数组? 我认为递归解决方案的复杂度分析是正确的,分别为O((n^2-n)/2)和O(n^2)。我的问题有两个。1)我的复杂度分析对递归和非递归解决方案都正确吗?2)是否存在一些高效或巧妙的方法来旋转矩阵,而我还没有发现?
谢谢您。
#include <stdio.h>
#include <stdlib.h>


int SIZE = 0;


/**
 * In-place, recursive, clockwise, 90 degree matrix rotation.
 */
static void rotate_in_place( int matrix[][SIZE], int n )
{
    if( n < 2 )
        return;


    int temp1, temp2;

    for( int i = 0; i < (n-1); i++ )
    {
        temp1 = matrix[i][n-1];
        matrix[i][n-1] = matrix[0][i];

        temp2 = matrix[n-1][n-i-1];
        matrix[n-1][n-i-1] = temp1;

        temp1 = matrix[n-i-1][0];
        matrix[n-i-1][0] = temp2;

        matrix[0][i] = temp1;
    }


    matrix = ((int*)matrix) + SIZE + 1;
    n -= 2;
    rotate_in_place( matrix, n );
}


static void print_matrix( int matrix[][SIZE] )
{
    printf( "\n" );
    for( int i = 0; i < SIZE; i++ )
    {
        for( int j = 0; j < SIZE; j++ )
            printf( "%4i ", matrix[i][j] );

        printf( "\n" );
    }
}


int main()
{

    // Create some matrices and rotate them.
    //
        int matrices = 10;

        for( int i = 2; i < matrices; i++ )
        {
            int matrix[i][i];

            int count = 0;
            for( int j = 0; j < i; j++ )
                for( int k = 0; k < i; k++ )
                    matrix[j][k] = ++count;


            printf( "\n\nRotating %ix%i matrix.\n", i, i );

            SIZE = i;

            printf( "\nOriginal matrix.\n" );
            print_matrix( matrix );

            rotate_in_place( matrix, i );

            printf( "\n\nRotated matrix.\n" );
            print_matrix( matrix );
        }


    return EXIT_SUCCESS;
}

5
你需要将n*n个元素移动到新的位置,因此很难想象它会比O(n^2)更少。 - Paul R
我几乎不认为这个解决方案是递归的。你可以轻易地用一个 goto 替换最后一次调用... - R.. GitHub STOP HELPING ICE
3个回答

3

以下是C语言的原地解决方案

void rotateRight(int matrix[][SIZE], int length) {

    int layer = 0;

    for (int layer = 0; layer < length / 2; ++layer) {

        int first = layer;
        int last = length - 1 - layer;

        for (int i = first; i < last; ++i) {

            int topline = matrix[first][i];
            int rightcol = matrix[i][last];
            int bottomline = matrix[last][length - layer - 1 - i];
            int leftcol = matrix[length - layer - 1 - i][first];

            matrix[first][i] = leftcol;
            matrix[i][last] = topline;
            matrix[last][length - layer - 1 - i] = rightcol;
            matrix[length - layer - 1 - i][first] = bottomline;
        }
    }
}

2

由于需要交换所有元素,因此旋转至少需要n^2次操作。通常情况下,由于旋转会对缓存造成很大的负担,我们会避免执行它 ;)


嗯,基本上是同一种问题,转置是围绕对角线进行的180度旋转。 - Joel Falcou
是的,除了关于对角元素的注释不适用于旋转的情况,因此在这种情况下可能会误导。 - Paul R

0

你的复杂度分析是正确的,但也很混乱。由于矩阵中元素的数量为n²,因此O(n²)实际上是与输入大小成线性关系的,这才是最重要的。

如果你想在旋转后打印整个矩阵,那么线性时间是你能做到的最好结果。对于其他操作,最好不要原地旋转矩阵,而是编写一个适配器来更改其索引,使得可以访问旋转后的矩阵元素,而无需在内存中进行实际旋转。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接