C++ vector操作について

## はじめに
動的に配列を確保できるvectorを最近よく利用しているので、vectorについてまとめていきたいと思います。

## vectorとは
他のオブジェクトを格納して情報の集まりを表すデータ構造のことを**コンテナ**といいます。C++の標準ライブラリでは多くのコンテナがあり、その中の1つが**ベクトル**を表す**vector<>ライブラリ**です。

## vector<>の利用法
**vector<型> 配列変数名;** で配列変数を宣言することができる。
```c++
vector<int> x;
```
<>の中には要素型であり、ここではint型としてxを生成しています。この生成されたxは、空の配列になります。
```c++
x.push_back(12);
x.push_back(13);
cout << "要素数：" << x.size() << endl;
cout << x[0] << endl;
cout << x[1] << endl;
```
```:結果
要素数：2
12
13
```
以上より、vectorの特徴を4つわかります。
①オブジェクト宣言時に要素数の指定が必要ない。
※vector<int> x(2); とすることで、要素数を明示的に指定することもできる。
　この形式で定義した場合、全要素が要素型のデフォルトコントラクタで初期化されます。
②push_back関数で末尾に要素を追加し、それに伴って要素数が自動的に増加する。
③要素数はsize関数を利用して調べることができる。
④各要素は添え字演算 **[ ]** でアクセスすることができる。

### ベクトルの構築-その他形式
```c++
// 要素数は12で全要素の初期値は3で初期化
vector<int> x2(12, 3);
// 配列a[0] ~ a[4] の5つをコピー
vector<int> x4(a, a + 5);
```

### 指定した位置の直前にxを挿入
**insert(i, x)** を利用することで、iのさあス一の直前にxを挿入する。
```c++
int main() {
    vector<int> array = {1, 2, 3};

    show(array);
    
    // 指定した位置に要素を挿入
    array.insert(array.begin() + 1, 10);

    show(array);
}
```
```:結果
{1 2 3 }
{1 10 2 3 }
```

### 末尾の要素を除去する

ベクトルの末尾に要素を追加するpush_back関数と逆で、**pop_back()** は末尾要素を除去します。
```c++
vector<int> x = {11, 12, 13};

cout << x.back() << endl;
// 末尾の要素除去
x.pop_back();
cout << x.back() << endl;
```
```:結果
13
12
```
末尾要素の値を調べる **back関数** を利用して値を出力します。先頭要素を調べる際は **front関数** を利用します。

### 指定した位置の要素を削除する
**erase(iterator i)** を利用することで、iの位置の要素を削除する。
```c++
#include <iostream>
#include <vector>
using namespace std;

// 配列の中身を表示
void show(const vector<int>& x) {
    cout << "{";
    for (int i = 0; i != x.size(); i++) {
        cout << x[i] << " ";
    }
    cout << "}\n";
}

int main() {
    vector<int> array = {1, 2, 3};

    show(array);

    // 指定した要素を削除
    array.erase(array.begin() + 1);

    show(array);
}
```
```:結果
{1 2 3 }
{1 3 }
```
また、範囲を指定することで範囲内にある要素を削除することができる。
```c++
// 配列の中身を表示
void show(const vector<int>& x) {
    cout << "{";
    for (int i = 0; i != x.size(); i++) {
        cout << x[i] << " ";
    }
    cout << "}\n";
}

int main() {
    // 指定した要素範囲を削除
    vector<int> array2 = {1, 2, 3, 4, 5};
    
    show(array2);

    array2.erase(array2.begin() + 1, array2.begin() + 3);

    show(array2);
}
```
```:結果
{1 2 3 4 5 }
{1 4 5 }
```
erase関数を利用して要素を削除すると、削除した要素より後ろにある要素が前に詰められる。


### atによる要素へのアクセス
添字演算子 [ ] だけでなく、at関数を利用して要素にアクセスすることも可能です。添字演算子 [ ] によるアクセスでは、添字の正当性のチェックが行われません。しかし、at関数を利用することで、 **不正な添字に対してout_of_range例外を送る** ことができます。

```c++
#include <iostream>
#include <vector>
using namespace std;

int main() {
    
    int a[] = {1, 2, 3};
    // sizeof(a) / sizeof(a[0]) で配列の要素数を取得
    vector<int> x(a, a + sizeof(a) / sizeof(a[0]));

    try {
        for (vector<int>::size_type i = 0; i <= 5; i++) {
            cout << "x["<< i << "] = " << x.at(i) << endl;
        }
    } catch (const out_of_range&) {
        cout << "取得に失敗しました。\n";
        return 1;
    }
}
```
ここで、**size_type型** は、ベクトルの要素数や要素の位置などを表す際に利用する符号無し整数型です。ベクトル内の個々の要素は、添字演算子 [] でアクセス可能ですが、添字の型も**vector<要素型>::size_type** になります。
```:結果
x[0] = 1
x[1] = 2
x[2] = 3
x[3] = 取得に失敗しました。
```

### ベクトルの全要素を削除する
**clear()** を利用することで、ベクトルの全要素を削除することができる。
```c++
// 配列の中身を表示
void show(const vector<int>& x) {
    cout << "{";
    for (int i = 0; i != x.size(); i++) {
        cout << x[i] << " ";
    }
    cout << "}\n";
}

int main() {
    int a[] = {1, 2, 3, 4, 5};
    vector<int> array(a, a + sizeof(a) / sizeof(a[0]));

    show(array);

    // 全要素削除
    array.clear();

    show(array);
}
```
```:結果
{1 2 3 4 5 }
{}
```
### 検索
algorithmライブラリの **find** を利用して、検索をする。
```c++

int main(void) {
    vector<string> vec = {"test", "code"};

    if (find(vec.begin(), vec.end(), "test") != vec.end()) {
        cout << "見つかった" << endl;
    } else {
        cout << "見つからない" << endl;
    }
}
```
```:結果
見つかった
```

### 並び替え
sort() : 小さい順に並び替え
reverse() : 配列を反転
```c++
void show(vector<int> x) {
    cout << "{ ";
    for (int i = 0; i < x.size(); i++) {
        cout << x[i] << " ";
    }
    cout << "} \n";
}

int main() {
    vector<int> vec = {10, 15, 5, 55, 25};

    show(vec);

    // 配列を反転
    reverse(vec.begin(), vec.end());
    cout << "配列を反転しました\n";

    show(vec);
    
    // 小さい順に並び替え
    sort(vec.begin(), vec.end());
    cout << "小さい順に並び替えました\n";

    show(vec);

    // 大きい順に並び替え
    sort(vec.begin(), vec.end(), greater<int>());
    cout << "大きい順に並び変えました\n";
    
    show(vec);
}

```
```:結果
{ 10 15 5 55 25 } 
配列を反転しました
{ 25 55 5 15 10 }
小さい順に並び替えました
{ 5 10 15 25 55 }
大きい順に並び変えました
{ 55 25 15 10 5 }
```


### ベクトルの全要素を入れ替える
**x.swap(y)** とすることで、xとyの全要素を入れ替えることができる。swap(x, y)としてもOKです。
```c++
int main() {
    int a[] = {1, 2, 3, 4, 5};
    vector<int> array(a, a + sizeof(a) / sizeof(a[0]));
    int b[] = {10, 20, 30};
    vector<int> array2(b, b + sizeof(b) / sizeof(b[0]));

    cout << "array = ";
    show(array);
    cout << "array2 = ";
    show(array2);

    // 全要素削除
    //array.clear();

    // arrayとarray2の全要素を入れ替える
    array.swap(array2);

    cout << "要素を入れ替えました\n";
    cout << "array = ";
    show(array);
    cout << "array2 = ";
    show(array2);
}
```
```:結果
array = {1 2 3 4 5 }
array2 = {10 20 30 }
要素を入れ替えました
array = {10 20 30 }
array2 = {1 2 3 4 5 }
 ```
 ### 容量
**size()** : サイズを返す
**resize()** : ベクトルの要素数サイズをszに変更する。
**capacity()** : 容量(サイズを変更することなく格納できる要素数)を返す
**empty()** : サイズが0であるかどうかを調べる
```c++
int main() {
    vector<int> array = {1, 2, 3, 4, 5};

    cout << "size() : " << array.size() << endl; // {1, 2, 3, 4, 5}
    array.resize(6);
    cout << "size() : " << array.size() << endl; // {1, 2, 3, 4, 5, 0}
    array.resize(2);
    cout << "size() : " << array.size() << endl; // {1, 2}
    array.resize(5, 10);
    cout << "size() : " << array.size() << endl; // {1, 2, 10, 10, 10}
    
 
    cout << "empty() : " << array.empty() << endl; // {1, 2, 10, 10, 10}
    array.clear();
    cout << "empty() : " << array.empty() << endl; // {}
    
}
```
```:結果
size() : 5
size() : 6
size() : 2
size() : 5
empty() : 0
empty() : 1
```
resize(sz, n)で、拡張する場合はベクトルが指定されたサイズになるまで末尾にnを追加する。nを指定しなかった場合は0を追加する。縮小する場合は、末尾から要素を削除する。


 ### 等価演算子と関係演算子
 2つのベクトルの等価性と大小関係を判定するために、**==, !=, <, <=, >, >=** が非メンバ関数として提供されています。
 ```c++
 #include <vector>
#include <iostream>
using namespace std;

/*
    等価演算子と関係演算子
*/
int main() {
    vector<int> x = {1, 2, 3, 4, 5};
    vector<int> y = {1, 2, 3, 4, 5};

    // xとyのサイズが同じで要素が等しい
    cout << "x == y : " << (x == y) << endl;
    // !(x == y)
    cout << "x != y : " << (x != y) << endl;

    // x < y
    cout << "x <  y : " << (x <  y) << endl;
    // x <= y
    cout << "x <= y : " << (x <= y) << endl;
    // x > y
    cout << "x >  y : " << (x >  y) << endl;
    // x >= y
    cout << "x >= y : " << (x >= y) << endl;
}

 ```
 ```:結果
x == y : 1
x != y : 0
x <  y : 0
x <= y : 1
x >  y : 0
x >= y : 1
 ```

 ### 要素のインデックスを検索
 特定要素のインデックスを取得したい時は、find関数とdistance関数を利用して取得することが可能です。
 ```c++
 int main() {
   vector<int> vec;
   vec.push_back(10);
   vec.push_back(20);
   vec.push_back(30);

   // イテレータを取得
   auto itr = find(vec.begin(), vec.end(), 20);
   // distance関数でイテレータ間の距離を求める
   int index = distance(vec.begin(), itr);

   cout << index << endl;
}
 ```
```:結果
1
```
①find関数でイテレータを取得します。
イテレータとは、コンテナ内の要素の位置を指すものです。ポインタと同じような扱いをすることができます。
②distance関数でイテレータ間の距離を求めます。
③求めた距離がインデックスの位置となります。
イテレータを使用できるコンテナは、vector以外にもmap、array、setなど他にもあります。

## ベクトルによる2次元配列
**vector<vector<型>> 配列変数名(要素数①, vector<型> (要素数②, 初期値));** で宣言することができる。

```c++
int main() {
    vector<vector<int>> array = {
        {1, 2},
        {3, 4},
        {5, 6}
    };

    // size()で縦の大きさを取得
    cout << "縦 : " << array.size() << endl;
    cout << "横 : " << array.at(0).size() << endl;
    cout << "3行2列目：" << array.at(2).at(1) << endl;
    
    // int[3][5]の配列を要素2で初期化
    vector<vector<int>> array2(3, vector<int> (5, 2));
    for (int i = 0; i < array2.size(); i++) {
        cout << "{ ";
        for (int j = 0; j < array2.at(0).size(); j++) {
            cout << array2.at(i).at(j) << " ";
        }
        cout << "}\n";
    }
}

```
```:結果
縦 : 3
横 : 2
3行2列目：6
{ 2 2 2 2 2 }
{ 2 2 2 2 2 }
{ 2 2 2 2 2 }
```

## 最後に
### ポインタと反復子
反復子によるベクトル要素の走査についての例を記載しておきます。
反復子に対して関節演算子*を適用できること、参照外しによって反復子が指す要素にアクセスできることが分かります。
```c++
int main() {
    // ベクトル要素
    vector<int> vec(3);
    int value2 = 0;
    for (vector<int>::iterator p = vec.begin(); p != vec.end(); p++) {
        // 入力
        *p = ++value;
    }
    for (vector<int>::iterator p = vec.begin(); p != vec.end(); p++) {
        // 出力
        cout << *p << " ";
    }
}
```
vectorは競プロの中でも頻繁に利用するライブラリなので、しっかりと復習して身に着けたいと思います。