二次元配列は、行と列の構造を持つデータ格納機構であり、C++では配列の配列として実現されます。この構造は行列的なデータ処理やゲーム開発、画像処理など、多次元データを扱う場面で広く利用されます。
宣言と初期化
二次元配列は、行数と列数を明示的に指定して宣言します。以下のように、静的サイズで宣言可能です:
int grid[5][8]; // 5行8列の整数配列
初期化は、複数の方法で行えます。リテラルによる初期化は直感的で効率的です:
int matrix[3][4] = {
{10, 20, 30, 40},
{50, 60, 70, 80},
{90, 100, 110, 120}
};
動的入力では、二重ループを用いてユーザー入力や外部データを格納します:
int data[3][4];
for (int row = 0; row < 3; ++row) {
for (int col = 0; col < 4; ++col) {
cin >> data[row][col];
}
}
要素のアクセスと出力
要素へのアクセスは、行インデックスと列インデックスの組み合わせで行われます。出力も同様に二重ループで実装します:
for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 4; ++j) {
cout << data[i][j] << " ";
}
cout << endl;
}
最大値の検出
配列内の最大値を探索するには、全要素を走査し、現在の最大値と比較します:
int maxValue = data[0][0];
for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 4; ++j) {
if (data[i][j] > maxValue) {
maxValue = data[i][j];
}
}
}
cout << "最大値: " << maxValue << endl;
各行のソート
二次元配列の各行を独立してソートするには、各インデックスに対してバブルソートを適用します:
void sortRows(int arr[][4], int rows) {
for (int r = 0; r < rows; ++r) {
for (int i = 0; i < 4 - 1; ++i) {
for (int j = 0; j < 4 - i - 1; ++j) {
if (arr[r][j] > arr[r][j + 1]) {
swap(arr[r][j], arr[r][j + 1]);
}
}
}
}
}
行列の90度回転(原地処理)
正方行列を効率よく回転させるには、転置と行の反転の組み合わせを利用します。この手法は追加メモリを必要とせず、空間計算量をO(1)に抑えられます。
void rotateSquareMatrix(vector<vector<int>>& mat) {
int n = mat.size();
// 転置: (i,j) ↔ (j,i)
for (int i = 0; i < n; ++i) {
for (int j = i + 1; j < n; ++j) {
swap(mat[i][j], mat[j][i]);
}
}
// 各行を反転
for (int i = 0; i < n; ++i) {
reverse(mat[i].begin(), mat[i].end());
}
}
このアルゴリズムは、次のような変換を実現します:
初期行列:
1 2 3
4 5 6
7 8 9
回転後:
7 4 1
8 5 2
9 6 3
行の挿入
二次元配列に新しい行を挿入するには、既存の要素を後方にシフトし、空いた位置に新しいデータを配置します。以下は末尾への挿入例です:
void insertRowAtEnd(int arr[][4], int& rows) {
if (rows < 5) { // 配列の最大行数を制限
int newRow[] = {13, 14, 15, 16};
for (int j = 0; j < 4; ++j) {
arr[rows][j] = newRow[j];
}
rows++;
}
}
中間挿入や先頭挿入も同様に、対象行以降の要素を右シフトして実現できます。
注意点
二次元配列は、メモリ上では連続的に配置されますが、各「行」は独立した一時配列として扱われます。そのため、関数引数として渡す際には列数を明示する必要があります:
void processGrid(int grid[][4], int rows); // OK
void processGrid(int grid[][], int rows); // エラー:列数未指定
また、標準ライブラリのstd::vector<std::vector<int>>を使用すれば、動的サイズや不均一な行長にも対応可能ですが、静的配列と比べてメモリ管理のオーバーヘッドが発生します。