N Log

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

문제

출처

int** solution(int num_list[], size_t num_list_len, int n);

정수 배열 num_list를 앞에서부터 n개씩 끊어 2차원 배열로 만드시오.
num_list_len은 항상 n의 배수이므로, 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차원으로 만들기이고 반환형은 int**다.
C에서 2차원 데이터를 표현할 때는 원소를 메모리에 연속해서 배치하는지에 따라 두 방식으로 나눌 수 있다.

첫 번째는 모든 원소를 하나의 연속된 메모리 블록에 저장하는 방식이다.
행과 열의 모양으로 접근하지만, 메모리에서는 1차원 배열처럼 원소가 차례로 놓인다.

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

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

두 번째는 행마다 별도의 1차원 배열을 할당하고, 행의 주소를 포인터 배열에 저장하는 방식이다.
각 행은 서로 다른 메모리 블록에 있으므로 원소 전체가 연속해서 놓이지 않는다.

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

연속 메모리 할당

#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(int*) * row_count);
    int* buffer = malloc(sizeof(int) * num_list_len);

    memcpy(buffer, num_list, sizeof(int) * num_list_len);

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

    return result;
}

num_list의 값을 하나의 연속된 buffer에 깊은 복사(deep copy)하고,
result의 각 행 포인터에는 buffer에서 col_count만큼 떨어진 주소를 저장한다.
그러면 실제 원소는 1차원으로 배치되어 있어도, 호출자는 result[row][col]처럼 2차원 배열과 같은 방식으로 접근할 수 있다.

result
   +------------+
0  | buffer + 0 | ----+
   +------------+     |
1  | buffer + 3 | ----|-----------+
   +------------+     |           |
2  | buffer + 6 | ----|-----------|-----------+
   +------------+     |           |           |
                      v           v           v
             buffer +---+---+---+---+---+---+---+---+---+
                    | 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]에는 buffer + 3이 저장되어 있다.
따라서 result[1][0]은 buffer[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

비연속 메모리 할당

#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(int*) * 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;
}

가독성을 위해 매개변수 n을 col_count에 담아 열 개수라는 의미를 드러냈다.
col_count는 한 행에 들어갈 원소 수이고, row_count는 전체 길이를 열 개수로 나눠 구한 행 개수다.

이 코드에서 주의할 점은 result와 result[row]의 자료형과 역할을 구분하는 것이다.
result는 int**로, 각 행을 가리키는 int*의 주소를 담는다.
반면 result[row]는 int*로, 해당 행의 int 원소를 저장할 공간을 가리킨다.