N Log

프로그래머스 입문 120842 2차원으로 만들기

문제

출처

정수 배열 num_list를 앞에서부터 n개씩 끊어 2차원 배열로 만드시오.
num_list의 길이는 항상 n의 배수이므로 남는 원소는 고려하지 않는다.

num_list n result
[1, 2, 3, 4, 5, 6, 7, 8] 2 [[1, 2], [3, 4], [5, 6], [7, 8]]
[100, 95, 2, 4, 5, 6, 18, 33, 948] 3 [[100, 95, 2], [4, 5, 6], [18, 33, 948]]

풀이

문제 이름이 2차원으로 만들기인데 2차원 배열과 관련해서는 두 가지가 있다.
직사각형 배열(rectangular array)과 가변 길이 행 배열(jagged array)이다.

직사각형 배열(rectangular array)은 전체 원소가 하나의 연속된 메모리 블록에 놓이는 형태다.

result
  |
  v
+---+---+---+
| 1 | 2 | 3 |
+---+---+---+
| 4 | 5 | 6 |
+---+---+---+
| 7 | 8 | 9 |
+---+---+---+

가변 길이 행 배열(jagged array)은 포인터들이 연속해서 놓여 있고, 각 포인터가 따로 할당된 배열을 가리키는 형태다.

result
  |
  v
+----+      +---+---+---+
| *--+----> | 1 | 2 | 3 |
+----+      +---+---+---+
| *--+----> | 4 | 5 | 6 |
+----+      +---+---+---+
| *--+----> | 7 | 8 | 9 |
+----+      +---+---+---+

행마다 길이가 다르면 더 전형적인 가변 길이 행 배열의 모습이 된다.

result
  |
  v
+----+      +---+
| *--+----> | 1 |
+----+      +---+
+----+      +---+---+---+
| *--+----> | 2 | 3 | 4 |
+----+      +---+---+---+
+----+      +---+---+
| *--+----> | 5 | 6 |
+----+      +---+---+
+----+      +---+---+---+----+
| *--+----> | 7 | 8 | 9 | 10 |
+----+      +---+---+---+----+

행별 개별 할당으로 2차원 배열 만들기

#include <stdlib.h>

int** solution(int num_list[], size_t num_list_len, int n)
{
    int col_count = n;
    int row_count = num_list_len / col_count;
    int** result = malloc(sizeof(*result) * row_count);

    for (int row = 0; row < row_count; row++)
    {
        result[row] = malloc(sizeof(int) * col_count);

        for (int col = 0; col < col_count; col++)
        {
            result[row][col] = num_list[row * col_count + col];
        }
    }

    return result;
}

가독성을 위해 매개변수 ncol_count에 담아 열 개수라는 의미를 드러냈다.
col_count는 한 행에 들어갈 원소 수이고, row_count는 전체 길이를 열 개수로 나눠 구한 행 개수다.
각 행의 주소를 저장하기 위해 result에는 행 개수만큼 공간을 할당한다.
이 공간은 행의 주소를 저장할 뿐, 각 행의 원소를 담는 공간은 아니다.
따라서 result[row]에는 한 행의 원소를 담을 공간을 따로 할당한다.

연속 메모리 블록을 행 포인터로 나누기

#include <stdlib.h>
#include <string.h>

int** solution(int num_list[], size_t num_list_len, int n)
{
    int col_count = n;
    int row_count = num_list_len / col_count;
    int** result = malloc(sizeof(*result) * row_count);
    int* data = malloc(sizeof(*data) * num_list_len);

    memcpy(data, num_list, sizeof(*data) * num_list_len);

    for (int row = 0; row < row_count; row++)
    {
        result[row] = data + row * col_count;
    }

    return result;
}

문제에서 반환형은 int**로 주어졌지만, 앞선 풀이와는 조금 다르게 접근해 볼 수 있다.
num_list를 하나의 연속된 메모리 블록에 복사하고, result에는 그 블록에서 col_count만큼씩 건너뛴 주소를 저장한다.
그러면 실제 값은 1차원으로 이어져 있어도, 접근 방식은 result[row][col]처럼 2차원 형태가 된다.

result
   +----------+
0  | data + 0 | ----+
   +----------+     |
1  | data + 3 | ----|-----------+
   +----------+     |           |
2  | data + 6 | ----|-----------|-----------+
   +----------+     |           |           |
                    v           v           v
             data +---+---+---+---+---+---+---+---+---+
                  | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
                  +---+---+---+---+---+---+---+---+---+
                    0   1   2   3   4   5   6   7   8
                    ^           ^           ^
                    |           |           |
                 result[0]   result[1]   result[2]

예를 들어 result[1]에는 data + 3이 저장되어 있다.
따라서 result[1][0]data[3]과 같고, 값은 4가 된다.

printf("%d\n", result[0][2]); // 3
printf("%d\n", result[1][0]); // 4
printf("%d\n", result[1][1]); // 5
printf("%d\n", *(result[1] + 1)); // 5