{ }codelper

学ぶ › TypeScript

さらに先へ · 18

2次元リスト(グリッド)

行と列のグリッド:読み込み、行の合計・列の合計、範囲内だけで隣のマスを見る。

2次元リストはリストを入れたリストです。
grid[i][j] は i 行目の j 列目のマスです。
下の例はグリッドを読み込み、行の合計と列の合計を求め、マスごとに隣のマスの合計を表示します。

import { readFileSync } from "node:fs";const [[r, c], ...grid]: number[][] = readFileSync(0, "utf8").trim().split("\n").map((l) => l.trim().split(/\s+/).map(Number));console.log(grid.map((row) => row.reduce((a, b) => a + b, 0)).join(" "));const colSums: number[] = Array.from({ length: c }, (_, j) => grid.reduce((s, row) => s + row[j], 0));console.log(colSums.join(" "));const moves: [number, number][] = [[-1, 0], [1, 0], [0, -1], [0, 1]];const near: number[][] = Array.from({ length: r }, () => new Array<number>(c).fill(0));for (let i = 0; i < r; i++) {  for (let j = 0; j < c; j++) {    for (const [di, dj] of moves) {      const ni = i + di, nj = j + dj;      if (ni >= 0 && ni < r && nj >= 0 && nj < c) near[i][j] += grid[ni][nj];    }  }}for (const row of near) console.log(row.join(" "));
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#・Craykon は範囲外エラーで止まってくれます。

いちばん上の行の「上の隣」を確認なしで読んだ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)) も同じなので、行は必ず一つずつ新しく作りましょう。

練習問題