N Log

2차원 배열 시계 방향 90도 회전

문제

배열을 입력받아 시계 방향으로 90도 회전된 결과를 만드는 함수를 작성하시오.

1 * 3

input:
[
    [ 1,  2,  3 ]
]

output:
[
    [ 1 ],
    [ 2 ],
    [ 3 ]
]

3 * 1

input:
[
    [  1 ],
    [ 11 ],
    [ 21 ]
]

output:
[
    [ 21, 11,  1 ]
]

3 * 3

input:
[
    [  1,  2,  3 ],
    [ 11, 12, 13 ],
    [ 21, 22, 23 ]
]

output:
[
    [ 21, 11,  1 ],
    [ 22, 12,  2 ],
    [ 23, 13,  3 ]
]

5 * 6

input:
[
    [  1,  2,  3,  4,  5,  6 ],
    [ 11, 12, 13, 14, 15, 16 ],
    [ 21, 22, 23, 24, 25, 26 ],
    [ 31, 32, 33, 34, 35, 36 ],
    [ 41, 42, 43, 44, 45, 46 ]
]

output:
[
    [ 41, 31, 21, 11,  1 ],
    [ 42, 32, 22, 12,  2 ],
    [ 43, 33, 23, 13,  3 ],
    [ 44, 34, 24, 14,  4 ],
    [ 45, 35, 25, 15,  5 ],
    [ 46, 36, 26, 16,  6 ]
]

풀이

#include <stdio.h>
#include <stdlib.h>

void RotateClockwise90Degrees(int row_count, int col_count, const int input[row_count][col_count], int output[col_count][row_count])
{
    for (int row = 0; row < row_count; row++)
    {
        for (int col = 0; col < col_count; col++)
        {
            output[col][row_count - 1 - row] = input[row][col];
        }
    }
}

void PrintMatrix(int row_count, int col_count, const int* matrix)
{
    for (int row = 0; row < row_count; row++)
    {
        for (int col = 0; col < col_count; col++)
        {
            printf("%3d", matrix[row * col_count + col]);
        }

        printf("\n");
    }
}

void RunExample(const char* title, int row_count, int col_count, const int input[row_count][col_count])
{
    int (*output)[row_count];

    output = (int (*)[row_count])malloc(sizeof(int) * row_count * col_count);

    if (output == NULL)
        return;

    printf("----- %s -----\n", title);
    printf("input:\n");
    PrintMatrix(row_count, col_count, input[0]);

    RotateClockwise90Degrees(row_count, col_count, input, output);

    printf("\noutput:\n");
    PrintMatrix(col_count, row_count, output[0]);
    printf("--------------------\n\n");

    free(output);
}

int main(void)
{
    const int input1[1][3] = {
        {  1,  2,  3 }
    };

    const int input2[3][1] = {
        {  1 },
        { 11 },
        { 21 }
    };

    const int input3[3][3] = {
        {  1,  2,  3 },
        { 11, 12, 13 },
        { 21, 22, 23 }
    };

    const int input4[5][6] = {
        {  1,  2,  3,  4,  5,  6 },
        { 11, 12, 13, 14, 15, 16 },
        { 21, 22, 23, 24, 25, 26 },
        { 31, 32, 33, 34, 35, 36 },
        { 41, 42, 43, 44, 45, 46 }
    };

    RunExample("1 * 3", 1, 3, input1);
    RunExample("3 * 1", 3, 1, input2);
    RunExample("3 * 3", 3, 3, input3);
    RunExample("5 * 6", 5, 6, input4);

    return 0;
}

원본 배열의 요소에 숫자 순서대로 접근하면, 현재 접근 중인 원소는 input[row][col]로 표현할 수 있다.
원본 배열에서는 열 인덱스인 col이 증가할수록 왼쪽에서 오른쪽으로 이동하지만, 결과 배열에서는 같은 값들이 행 인덱스가 증가하는 방향으로 놓인다.
따라서 결과 배열에서는 원본 배열의 열 인덱스인 col이 행 인덱스가 되므로 output[col][?] = input[row][col]; 형태가 된다.

이제 ?에 들어갈 결과 배열의 열 인덱스를 생각해보자.
5x6 배열을 기준으로 보면 원본 배열의 0번 행은 결과 배열의 마지막 열인 4번 열에 놓이고, 원본 배열의 1번 행은 3번 열에 놓인다.
원본 배열에서는 행 인덱스가 고정된 상태로 열 인덱스가 증가하지만, 결과 배열에서는 열 인덱스가 고정된 상태로 행 인덱스가 증가한다.
결과 배열의 마지막 열 인덱스는 row_count - 1이고, 원본 배열의 행 인덱스인 row가 증가할 때마다 결과 배열의 열 인덱스는 하나씩 줄어들어야 한다.
그래서 결과 배열의 열 인덱스는 row_count - 1 - row가 된다.

따라서 최종 식은 다음과 같다.

output[col][row_count - 1 - row] = input[row][col];

VLA를 지원하지 않는 환경에서는 다음처럼 포인터를 사용해 작성할 수도 있다.

void RotateClockwise90Degrees(int row_count, int col_count, const int* input, int* output)
{
    for (int row = 0; row < row_count; row++)
    {
        for (int col = 0; col < col_count; col++)
        {
            output[col * row_count + (row_count - 1 - row)] = input[row * col_count + col];
        }
    }
}