{ }codelper

배우기 › C

더 나아가기 · 18

2차원 목록(격자)

줄과 칸으로 된 격자: 읽기, 줄 합·칸 합, 이웃 칸을 범위 안에서만 보기.

2차원 목록은 목록을 담은 목록이에요.
grid[i][j]는 i번째 줄의 j번째 칸이에요.
아래 예제는 격자를 읽어 줄 합과 칸 합을 구하고, 칸마다 이웃 칸의 합을 찍어요.

#include <stdio.h> #define MAX 100 int grid[MAX][MAX]; void print(const int *a, int n) {    for (int i = 0; i < n; i++) printf(i > 0 ? " %d" : "%d", a[i]);    printf("\n");} int main(void) {    int r, c, rowSums[MAX] = {0}, colSums[MAX] = {0};    scanf("%d %d", &r, &c);    for (int i = 0; i < r; i++)        for (int j = 0; j < c; j++) {            scanf("%d", &grid[i][j]);            rowSums[i] += grid[i][j];            colSums[j] += grid[i][j];        }    print(rowSums, r);    print(colSums, c);    const int di[] = {-1, 1, 0, 0}, dj[] = {0, 0, -1, 1};    for (int i = 0; i < r; i++) {        int near[MAX] = {0};        for (int j = 0; j < c; j++)            for (int k = 0; k < 4; k++) {                int ni = i + di[k], nj = j + dj[k];                if (ni >= 0 && ni < r && nj >= 0 && nj < c) near[j] += grid[ni][nj];            }        print(near, c);    }    return 0;}
stdin
3 4
1 2 3 4
5 6 7 8
9 10 11 12
stdout
10 26 42
15 18 21 24
7 10 13 11
16 24 28 23
15 26 29 19

핵심

  • 모든 칸을 보려면 줄을 도는 반복 안에 칸을 도는 반복을 넣어요(겹친 반복).
  • 줄 합은 줄마다 칸을 더하고, 칸 합은 칸마다 줄을 따라 내려가며 더해요.
    한 번 돌면서 rowSums[i]와 colSums[j]에 함께 더해도 돼요.
  • 이웃 칸(위·아래·왼쪽·오른쪽)은 방향을 di = [-1, 1, 0, 0], dj = [0, 0, -1, 1]처럼 목록으로 두고 돌면 짧아져요.
  • 가장자리에서는 이웃이 바깥으로 나가요.
    읽기 전에 꼭 0 <= ni < r이고 0 <= nj < c인지 확인해요.
    음수 쪽도요.
  • 줄은 하나씩 새로 만들어야 해요.
    같은 줄을 여러 번 넣으면 한 칸만 바꿔도 모든 줄이 바뀌어요.

언어별로 쓰는 법

Python[[0] * c for _ in range(r)].
grid[-1]은 오류가 아니라 마지막 줄이에요
JavaScriptArray.from({ length: r }, () => new Array(c).fill(0)).
바깥 칸은 undefined, 바깥 줄에서 칸을 읽으면 TypeError
TypeScriptJavaScript와 같아요.
타입은 number[][]
Javanew int[r][c] — 모두 0으로 채워져요.
범위 밖은 ArrayIndexOutOfBoundsException
C#직사각형 배열 int[r, c](grid[i, j]로 읽어요)와 배열의 배열 int[r][] 두 가지가 있어요.
범위 밖은 IndexOutOfRangeException
C++vector<vector<int>>(r, vector<int>(c)).
[]는 범위를 확인하지 않아요
Cint grid[100][100]처럼 크기를 미리 정해 두고 앞부분만 써요.
범위 밖을 읽어도 아무 말이 없어요
Craykon줄마다 grid.넣기(콘솔.수들읽기())로 쌓고, 새목록(c, 0)으로 0이 c개인 줄을 만들어요.
범위 밖은 "목록에는 0~3번만 있어요" 오류

자주 하는 실수

범위 확인을 빼먹었어요JavaScript · TypeScript · C · C++

const grid = [[1, 2], [3, 4]];
console.log(1 + grid[0][2]);
console.log(grid[2][0]);

→ NaN이 찍히고, 다음 줄은 TypeError로 멈춰요. C·C++은 아무 말 없이 옆 줄이나 엉뚱한 메모리를 읽어요

읽기 전에 0 <= ni && ni < r && 0 <= nj && nj < c를 확인해요.
Java·C#·크레이콘은 범위 오류로 멈춰 줘요.

맨 윗줄의 위 이웃을 확인 없이 읽었어요Python

grid = [[1, 2], [3, 4]]
i, j = 0, 0
print(grid[i - 1][j])

→ 3 — grid[-1]은 마지막 줄이라 오류 없이 맨 아랫줄 값을 읽어요

0 <= ni < r처럼 음수 쪽도 꼭 확인해요.

언어마다 다른 점
Python [[0] * c] * r는 같은 줄 하나를 r번 가리켜서, g = [[0] * 3] * 2; g[0][0] = 5 하면 [[5, 0, 0], [5, 0, 0]]이 돼요.
JavaScript new Array(r).fill(new Array(c).fill(0))도 똑같으니, 줄은 꼭 하나씩 새로 만드세요.

연습 문제