[C++] 2次元vectorの初期化方法と活用法

C++で2次元vectorを初期化する方法は複数あります。

最も基本的な方法は、コンストラクタを使用してサイズを指定する方法です。

例えば、vector<vector<int>> matrix(rows, vector<int>(cols, initialValue));とすると、rowscols列の2次元vectorがinitialValueで初期化されます。

また、リスト初期化を用いてvector<vector<int>> matrix = {{1, 2}, {3, 4}};のように直接値を指定することも可能です。

2次元vectorは、行列のようなデータ構造を扱う際に便利で、動的にサイズを変更できるため、柔軟なデータ管理が可能です。

例えば、グラフの隣接行列やゲームの盤面の表現に活用されます。

この記事でわかること
  • 2次元vectorの初期化方法: コンストラクタ、リスト初期化、ループ、fill関数
  • 2次元vectorの操作: 要素のアクセス、追加と削除、サイズの変更、イテレーション
  • 2次元vectorの活用例: 行列、グラフの隣接行列、ゲームの盤面、データテーブルの管理
  • 2次元vectorの応用例: 画像処理、パスファインディング、データ解析
  • 2次元vectorの特徴: 動的にサイズを変更可能、メモリ管理が自動で柔軟なデータ構造

目次から探す

2次元vectorの基礎知識

C++における2次元vectorは、動的にサイズを変更できる便利なデータ構造です。

通常の配列と異なり、vectorはメモリ管理を自動で行い、要素の追加や削除が容易です。

2次元vectorは、vectorの中にvectorを格納することで実現され、行列や表のようなデータを扱う際に非常に役立ちます。

例えば、行列演算やゲームの盤面管理、データテーブルの管理など、さまざまな場面で活用されています。

2次元vectorを効果的に利用するためには、初期化方法や要素のアクセス方法を理解することが重要です。

これにより、効率的なプログラムを作成することが可能になります。

2次元vectorの初期化方法

2次元vectorの初期化方法にはいくつかの方法があります。

それぞれの方法を理解することで、用途に応じた最適な初期化が可能になります。

コンストラクタを用いた初期化

コンストラクタを用いることで、2次元vectorを指定したサイズで初期化することができます。

以下の例では、3行4列の2次元vectorを初期化しています。

#include <iostream>
#include <vector>
int main() {
    // 3行4列の2次元vectorを0で初期化
    std::vector<std::vector<int>> matrix(3, std::vector<int>(4, 0));
    // 初期化された2次元vectorを出力
    for (const auto& row : matrix) {
        for (int value : row) {
            std::cout << value << " ";
        }
        std::cout << std::endl;
    }
    return 0;
}
0 0 0 0 
0 0 0 0 
0 0 0 0

この方法では、すべての要素が指定した初期値で埋められます。

リスト初期化

リスト初期化を用いると、2次元vectorを直接初期化リストで初期化することができます。

以下の例では、異なる値を持つ2次元vectorを初期化しています。

#include <iostream>
#include <vector>
int main() {
    // リスト初期化を用いて2次元vectorを初期化
    std::vector<std::vector<int>> matrix = {
        {1, 2, 3},
        {4, 5, 6},
        {7, 8, 9}
    };
    // 初期化された2次元vectorを出力
    for (const auto& row : matrix) {
        for (int value : row) {
            std::cout << value << " ";
        }
        std::cout << std::endl;
    }
    return 0;
}
1 2 3 
4 5 6 
7 8 9

リスト初期化は、特定の値で2次元vectorを初期化したい場合に便利です。

ループを用いた動的初期化

ループを用いることで、動的に2次元vectorを初期化することができます。

以下の例では、行と列のインデックスを用いて初期化しています。

#include <iostream>
#include <vector>
int main() {
    int rows = 3;
    int cols = 4;
    std::vector<std::vector<int>> matrix(rows, std::vector<int>(cols));
    // ループを用いて動的に初期化
    for (int i = 0; i < rows; ++i) {
        for (int j = 0; j < cols; ++j) {
            matrix[i][j] = i * cols + j;
        }
    }
    // 初期化された2次元vectorを出力
    for (const auto& row : matrix) {
        for (int value : row) {
            std::cout << value << " ";
        }
        std::cout << std::endl;
    }
    return 0;
}
0 1 2 3 
4 5 6 7 
8 9 10 11

この方法は、計算に基づいて要素を初期化したい場合に有用です。

fill関数を用いた初期化

fill関数を用いることで、2次元vectorのすべての要素を特定の値で埋めることができます。

以下の例では、すべての要素を5で初期化しています。

#include <iostream>
#include <vector>
#include <algorithm>
int main() {
    int rows = 3;
    int cols = 4;
    std::vector<std::vector<int>> matrix(rows, std::vector<int>(cols));
    // fill関数を用いてすべての要素を5で初期化
    for (auto& row : matrix) {
        std::fill(row.begin(), row.end(), 5);
    }
    // 初期化された2次元vectorを出力
    for (const auto& row : matrix) {
        for (int value : row) {
            std::cout << value << " ";
        }
        std::cout << std::endl;
    }
    return 0;
}
5 5 5 5 
5 5 5 5 
5 5 5 5

fill関数を用いると、特定の値で2次元vectorを効率的に初期化することができます。

2次元vectorの操作

2次元vectorを効果的に利用するためには、要素のアクセスや操作方法を理解することが重要です。

ここでは、基本的な操作方法について説明します。

要素のアクセス方法

2次元vectorの要素にアクセスするには、行と列のインデックスを指定します。

以下の例では、特定の要素にアクセスして値を取得および設定しています。

#include <iostream>
#include <vector>
int main() {
    std::vector<std::vector<int>> matrix = {
        {1, 2, 3},
        {4, 5, 6},
        {7, 8, 9}
    };
    // 要素にアクセスして値を取得
    int value = matrix[1][2]; // 2行3列目の要素
    std::cout << "取得した値: " << value << std::endl;
    // 要素にアクセスして値を設定
    matrix[0][0] = 10; // 1行1列目の要素を10に設定
    // 更新された2次元vectorを出力
    for (const auto& row : matrix) {
        for (int val : row) {
            std::cout << val << " ";
        }
        std::cout << std::endl;
    }
    return 0;
}
取得した値: 6
10 2 3 
4 5 6 
7 8 9

このように、行と列のインデックスを指定することで、特定の要素にアクセスできます。

要素の追加と削除

2次元vectorに要素を追加したり削除したりすることも可能です。

以下の例では、行や列を追加・削除しています。

#include <iostream>
#include <vector>
int main() {
    std::vector<std::vector<int>> matrix = {
        {1, 2, 3},
        {4, 5, 6}
    };
    // 行を追加
    matrix.push_back({7, 8, 9});
    // 列を追加
    for (auto& row : matrix) {
        row.push_back(0); // 各行の末尾に0を追加
    }
    // 行を削除
    matrix.pop_back();
    // 列を削除
    for (auto& row : matrix) {
        row.pop_back(); // 各行の末尾の要素を削除
    }
    // 更新された2次元vectorを出力
    for (const auto& row : matrix) {
        for (int val : row) {
            std::cout << val << " ";
        }
        std::cout << std::endl;
    }
    return 0;
}
1 2 3 
4 5 6

この例では、行や列の追加・削除を行うことで、2次元vectorのサイズを動的に変更しています。

サイズの変更

2次元vectorのサイズを変更するには、resizeメソッドを使用します。

以下の例では、行と列のサイズを変更しています。

#include <iostream>
#include <vector>
int main() {
    std::vector<std::vector<int>> matrix(2, std::vector<int>(3, 0));
    // 行のサイズを変更
    matrix.resize(3);
    // 各行の列のサイズを変更
    for (auto& row : matrix) {
        row.resize(4, 1); // 新しい要素を1で初期化
    }
    // 更新された2次元vectorを出力
    for (const auto& row : matrix) {
        for (int val : row) {
            std::cout << val << " ";
        }
        std::cout << std::endl;
    }
    return 0;
}
0 0 0 1 
0 0 0 1 
1 1 1 1

resizeメソッドを使用することで、行や列のサイズを柔軟に変更できます。

イテレーションの方法

2次元vectorの要素をイテレーションするには、ネストされたループを使用します。

以下の例では、すべての要素を出力しています。

#include <iostream>
#include <vector>
int main() {
    std::vector<std::vector<int>> matrix = {
        {1, 2, 3},
        {4, 5, 6},
        {7, 8, 9}
    };
    // 2次元vectorの要素をイテレーション
    for (const auto& row : matrix) {
        for (int val : row) {
            std::cout << val << " ";
        }
        std::cout << std::endl;
    }
    return 0;
}
1 2 3 
4 5 6 
7 8 9

このように、ネストされたループを用いることで、2次元vectorのすべての要素にアクセスできます。

2次元vectorの活用法

2次元vectorは、さまざまな場面で活用できる柔軟なデータ構造です。

ここでは、具体的な活用例をいくつか紹介します。

行列の表現

2次元vectorは、行列を表現するのに最適です。

行列演算やデータの格納に利用できます。

以下の例では、2つの行列を加算しています。

#include <iostream>
#include <vector>
int main() {
    std::vector<std::vector<int>> matrixA = {
        {1, 2, 3},
        {4, 5, 6}
    };
    std::vector<std::vector<int>> matrixB = {
        {7, 8, 9},
        {10, 11, 12}
    };
    std::vector<std::vector<int>> result(2, std::vector<int>(3, 0));
    // 行列の加算
    for (size_t i = 0; i < matrixA.size(); ++i) {
        for (size_t j = 0; j < matrixA[i].size(); ++j) {
            result[i][j] = matrixA[i][j] + matrixB[i][j];
        }
    }
    // 結果を出力
    for (const auto& row : result) {
        for (int val : row) {
            std::cout << val << " ";
        }
        std::cout << std::endl;
    }
    return 0;
}
8 10 12 
14 16 18

この例では、2つの行列を要素ごとに加算し、新しい行列を生成しています。

グラフの隣接行列

グラフの隣接行列を表現するのにも2次元vectorは便利です。

以下の例では、無向グラフの隣接行列を作成しています。

#include <iostream>
#include <vector>
int main() {
    // 4頂点の無向グラフの隣接行列
    std::vector<std::vector<int>> adjacencyMatrix = {
        {0, 1, 0, 1},
        {1, 0, 1, 0},
        {0, 1, 0, 1},
        {1, 0, 1, 0}
    };
    // 隣接行列を出力
    for (const auto& row : adjacencyMatrix) {
        for (int val : row) {
            std::cout << val << " ";
        }
        std::cout << std::endl;
    }
    return 0;
}
0 1 0 1 
1 0 1 0 
0 1 0 1 
1 0 1 0

この隣接行列は、頂点間の接続を示しており、グラフの解析に利用できます。

ゲームの盤面管理

ゲームの盤面を管理するのにも2次元vectorは適しています。

以下の例では、簡単なチェス盤を表現しています。

#include <iostream>
#include <vector>
int main() {
    // 8x8のチェス盤を表現
    std::vector<std::vector<char>> chessBoard(8, std::vector<char>(8, '-'));
    // 初期配置
    chessBoard[0][0] = 'R'; // ルーク
    chessBoard[0][1] = 'N'; // ナイト
    chessBoard[0][2] = 'B'; // ビショップ
    chessBoard[0][3] = 'Q'; // クイーン
    chessBoard[0][4] = 'K'; // キング
    chessBoard[0][5] = 'B'; // ビショップ
    chessBoard[0][6] = 'N'; // ナイト
    chessBoard[0][7] = 'R'; // ルーク
    // チェス盤を出力
    for (const auto& row : chessBoard) {
        for (char piece : row) {
            std::cout << piece << " ";
        }
        std::cout << std::endl;
    }
    return 0;
}
R N B Q K B N R 
- - - - - - - - 
- - - - - - - - 
- - - - - - - - 
- - - - - - - - 
- - - - - - - - 
- - - - - - - - 
- - - - - - - -

この例では、チェスの駒を配置し、盤面を管理しています。

データテーブルの管理

2次元vectorは、データテーブルの管理にも利用できます。

以下の例では、簡単な成績表を作成しています。

#include <iostream>
#include <vector>
#include <string>
int main() {
    // 学生の成績表
    std::vector<std::vector<std::string>> gradeTable = {
        {"名前", "数学", "英語", "科学"},
        {"田中", "85", "78", "92"},
        {"佐藤", "90", "88", "85"},
        {"鈴木", "75", "82", "89"}
    };
    // 成績表を出力
    for (const auto& row : gradeTable) {
        for (const std::string& item : row) {
            std::cout << item << " ";
        }
        std::cout << std::endl;
    }
    return 0;
}
名前 数学 英語 科学 
田中 85 78 92 
佐藤 90 88 85 
鈴木 75 82 89

この例では、学生の名前と成績を2次元vectorで管理し、出力しています。

応用例

2次元vectorは、さまざまな応用分野で活用されています。

ここでは、具体的な応用例をいくつか紹介します。

2次元vectorを用いた画像処理

画像処理では、画像をピクセル単位で操作する必要があります。

2次元vectorを用いることで、画像を効率的に管理できます。

以下の例では、簡単なグレースケール画像を反転しています。

#include <iostream>
#include <vector>
int main() {
    // 3x3のグレースケール画像を表現
    std::vector<std::vector<int>> image = {
        {255, 128, 0},
        {64, 192, 32},
        {16, 48, 96}
    };
    // 画像の反転処理
    for (auto& row : image) {
        for (int& pixel : row) {
            pixel = 255 - pixel; // ピクセル値を反転
        }
    }
    // 反転された画像を出力
    for (const auto& row : image) {
        for (int pixel : row) {
            std::cout << pixel << " ";
        }
        std::cout << std::endl;
    }
    return 0;
}
0 127 255 
191 63 223 
239 207 159

この例では、各ピクセルの値を反転することで、画像のネガを生成しています。

2次元vectorを用いたパスファインディング

パスファインディングアルゴリズムでは、2次元vectorを用いてグリッドを表現し、最短経路を探索します。

以下の例では、簡単なグリッド上でのパスファインディングを示しています。

#include <iostream>
#include <vector>
#include <queue>
struct Point {
    int x, y;
};
int main() {
    // 5x5のグリッドを表現(0は通行可能、1は障害物)
    std::vector<std::vector<int>> grid = {
        {0, 0, 1, 0, 0},
        {0, 1, 0, 1, 0},
        {0, 0, 0, 0, 0},
        {1, 0, 1, 0, 1},
        {0, 0, 0, 0, 0}
    };
    // スタートとゴールの座標
    Point start = {0, 0};
    Point goal = {4, 4};
    // パスファインディングの実装(幅優先探索)
    std::queue<Point> q;
    q.push(start);
    grid[start.x][start.y] = 2; // 訪問済みを示す
    while (!q.empty()) {
        Point current = q.front();
        q.pop();
        if (current.x == goal.x && current.y == goal.y) {
            std::cout << "ゴールに到達しました!" << std::endl;
            break;
        }
        // 移動可能な方向(上下左右)
        std::vector<Point> directions = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
        for (const auto& dir : directions) {
            int newX = current.x + dir.x;
            int newY = current.y + dir.y;
            if (newX >= 0 && newX < grid.size() && newY >= 0 && newY < grid[0].size() && grid[newX][newY] == 0) {
                q.push({newX, newY});
                grid[newX][newY] = 2; // 訪問済みを示す
            }
        }
    }
    return 0;
}

この例では、幅優先探索を用いて、グリッド上のスタートからゴールまでの経路を探索しています。

2次元vectorを用いたデータ解析

データ解析では、2次元vectorを用いてデータセットを管理し、統計的な処理を行います。

以下の例では、データセットの平均値を計算しています。

#include <iostream>
#include <vector>
int main() {
    // データセットを表現
    std::vector<std::vector<double>> data = {
        {1.2, 2.3, 3.4},
        {4.5, 5.6, 6.7},
        {7.8, 8.9, 9.0}
    };
    // 各列の平均値を計算
    std::vector<double> columnAverages(data[0].size(), 0.0);
    for (const auto& row : data) {
        for (size_t i = 0; i < row.size(); ++i) {
            columnAverages[i] += row[i];
        }
    }
    for (double& avg : columnAverages) {
        avg /= data.size();
    }
    // 平均値を出力
    std::cout << "各列の平均値: ";
    for (double avg : columnAverages) {
        std::cout << avg << " ";
    }
    std::cout << std::endl;
    return 0;
}
各列の平均値: 4.5 5.6 6.36667

この例では、データセットの各列の平均値を計算し、出力しています。

2次元vectorを用いることで、データの管理と解析が容易になります。

よくある質問

2次元vectorのサイズを動的に変更するには?

2次元vectorのサイズを動的に変更するには、resizeメソッドを使用します。

行のサイズを変更するには、外側のvectorに対してresizeを呼び出します。

各行の列のサイズを変更するには、各行のvectorに対してresizeを呼び出します。

例えば、matrix.resize(newRowSize);で行のサイズを変更し、matrix[i].resize(newColSize);で列のサイズを変更できます。

2次元vectorのメモリ使用量を最適化する方法は?

2次元vectorのメモリ使用量を最適化するためには、以下の方法を考慮できます:

  1. 初期サイズの設定: 必要なサイズを事前に見積もり、初期化時に適切なサイズを設定します。

これにより、再割り当ての回数を減らせます。

  1. shrink_to_fitの使用: メモリを解放するために、shrink_to_fitメソッドを使用して、未使用のメモリを解放します。
  2. 要素の削除: 不要な要素を削除し、メモリを効率的に使用します。

2次元vectorと配列の違いは何ですか?

2次元vectorと配列にはいくつかの違いがあります:

  • サイズの柔軟性: vectorは動的にサイズを変更できるのに対し、配列は固定サイズです。
  • メモリ管理: vectorは自動的にメモリを管理し、必要に応じて再割り当てを行いますが、配列は手動で管理する必要があります。
  • 使いやすさ: vectorはSTLの一部であり、多くの便利なメソッドが用意されていますが、配列は基本的な操作しかサポートしていません。
  • パフォーマンス: 配列はメモリの連続領域を使用するため、アクセスが高速ですが、vectorは柔軟性のために若干のオーバーヘッドがあります。

まとめ

この記事では、C++における2次元vectorの初期化方法や操作方法、さらには具体的な活用例について詳しく解説しました。

2次元vectorは、行列の表現やグラフの隣接行列、ゲームの盤面管理、データテーブルの管理など、さまざまな場面でその柔軟性と利便性を発揮します。

これらの知識を活かして、実際のプログラミングにおいて2次元vectorを積極的に活用し、より効率的で効果的なコードを書いてみてください。

  • URLをコピーしました!
目次から探す