[C++] std::dequeの使い方について詳しく解説
std::deque
はC++標準ライブラリに含まれるコンテナで、両端からの高速な挿入と削除が可能です。
このコンテナは、動的配列のようにランダムアクセスが可能であり、std::vector
と似たインターフェースを持っていますが、メモリの再配置が少ないため、特定のシナリオで効率的です。
メソッドにはpush_back
、push_front
、pop_back
、pop_front
などがあり、これらを使って要素を簡単に操作できます。
また、at
やoperator[]
を用いて要素にアクセスすることも可能です。
std::deque
の基本的な特性と利点- 要素の追加、削除、アクセス方法
std::deque
の主要なメソッドの使い方- キュー、スタック、双方向リストとしての利用方法
- スレッドセーフ性やメモリ効率、パフォーマンスに関する情報
std::dequeとは
std::deque
(ダブルエンドキュー)は、C++の標準ライブラリに含まれるコンテナの一つで、両端からの要素の追加や削除が効率的に行えるデータ構造です。
deque
は、動的配列のように要素を格納しますが、両端からの操作が可能なため、特にキューやスタックの実装に適しています。
std::dequeの基本概念
- 両端操作:
std::deque
は、先頭と末尾の両方から要素を追加・削除できます。 - 動的サイズ: 要素数に応じて自動的にサイズが調整されます。
- ランダムアクセス: インデックスを使用して任意の位置の要素にアクセスできます。
std::dequeとstd::vectorの違い
特徴 | std::deque | std::vector |
---|---|---|
要素の追加・削除 | 両端から可能 | 末尾のみ |
メモリ管理 | 非連続的にメモリを使用 | 連続的にメモリを使用 |
アクセス速度 | 一般的に遅い | 高速 |
サイズ変更のコスト | 高い(再配置が必要な場合あり) | 低い(末尾の追加は効率的) |
std::dequeの利点と欠点
- 利点:
- 両端からの要素操作が可能で、キューやスタックの実装に便利。
- サイズが動的に変化し、必要に応じてメモリを確保。
- 欠点:
- メモリの使用効率が
std::vector
に比べて劣る場合がある。 - ランダムアクセスの速度が遅くなることがある。
std::dequeの基本操作
std::deque
は、要素の追加、削除、アクセス、サイズの取得や変更が簡単に行える便利なコンテナです。
以下では、これらの基本操作について詳しく解説します。
要素の追加と削除
std::deque
では、要素を両端から追加・削除するためのメソッドが用意されています。
以下は、主なメソッドの一覧です。
操作 | メソッド名 | 説明 |
---|---|---|
末尾に追加 | push_back | 要素を末尾に追加 |
先頭に追加 | push_front | 要素を先頭に追加 |
末尾から削除 | pop_back | 末尾の要素を削除 |
先頭から削除 | pop_front | 先頭の要素を削除 |
以下は、要素の追加と削除のサンプルコードです。
#include <iostream>
#include <deque>
int main() {
std::deque<std::string> dq;
dq.push_back("要素1");
dq.push_front("要素0");
dq.push_back("要素2");
std::cout << "先頭の要素: " << dq.front() << std::endl; // 要素0
std::cout << "末尾の要素: " << dq.back() << std::endl; // 要素2
dq.pop_front(); // 要素0を削除
dq.pop_back(); // 要素2を削除
std::cout << "残っている要素: " << dq.front() << std::endl; // 要素1
return 0;
}
先頭の要素: 要素0
末尾の要素: 要素2
残っている要素: 要素1
要素へのアクセス
std::deque
では、要素へのアクセスも簡単に行えます。
以下のメソッドを使用します。
操作 | メソッド名 | 説明 |
---|---|---|
インデックスアクセス | operator[] | 指定したインデックスの要素にアクセス |
範囲チェック付きアクセス | at | 指定したインデックスの要素にアクセス(範囲チェックあり) |
先頭の要素 | front | 先頭の要素を取得 |
末尾の要素 | back | 末尾の要素を取得 |
以下は、要素へのアクセスのサンプルコードです。
#include <iostream>
#include <deque>
int main() {
std::deque<int> dq = {10, 20, 30, 40, 50};
std::cout << "インデックス1の要素: " << dq[1] << std::endl; // 20
std::cout << "インデックス2の要素: " << dq.at(2) << std::endl; // 30
std::cout << "先頭の要素: " << dq.front() << std::endl; // 10
std::cout << "末尾の要素: " << dq.back() << std::endl; // 50
return 0;
}
インデックス1の要素: 20
インデックス2の要素: 30
先頭の要素: 10
末尾の要素: 50
サイズの取得と変更
std::deque
のサイズを取得したり、サイズを変更したりするためのメソッドも用意されています。
操作 | メソッド名 | 説明 |
---|---|---|
サイズの取得 | size | 現在の要素数を取得 |
サイズの変更 | resize | 要素数を指定したサイズに変更 |
全要素の削除 | clear | 全ての要素を削除 |
以下は、サイズの取得と変更のサンプルコードです。
#include <iostream>
#include <deque>
int main() {
std::deque<int> dq = {1, 2, 3, 4, 5};
std::cout << "初期サイズ: " << dq.size() << std::endl; // 5
dq.resize(3); // サイズを3に変更
std::cout << "サイズ変更後: " << dq.size() << std::endl; // 3
dq.clear(); // 全要素を削除
std::cout << "クリア後のサイズ: " << dq.size() << std::endl; // 0
return 0;
}
初期サイズ: 5
サイズ変更後: 3
クリア後のサイズ: 0
std::dequeのメソッド
std::deque
は、さまざまなメソッドを提供しており、要素の追加、削除、アクセス、サイズの管理が容易に行えます。
以下では、主要なメソッドについて詳しく解説します。
push_backとpush_front
push_back
: 要素を末尾に追加します。push_front
: 要素を先頭に追加します。
これらのメソッドは、std::deque
の両端からの操作を可能にします。
#include <iostream>
#include <deque>
int main() {
std::deque<std::string> dq;
dq.push_back("要素1");
dq.push_front("要素0");
dq.push_back("要素2");
for (const auto& elem : dq) {
std::cout << elem << " "; // 要素0 要素1 要素2
}
std::cout << std::endl;
return 0;
}
要素0 要素1 要素2
pop_backとpop_front
pop_back
: 末尾の要素を削除します。pop_front
: 先頭の要素を削除します。
これらのメソッドを使用することで、std::deque
の両端から要素を削除できます。
#include <iostream>
#include <deque>
int main() {
std::deque<int> dq = {1, 2, 3, 4, 5};
dq.pop_front(); // 先頭の要素を削除
dq.pop_back(); // 末尾の要素を削除
for (const auto& elem : dq) {
std::cout << elem << " "; // 2 3 4
}
std::cout << std::endl;
return 0;
}
2 3 4
atとoperator[]
at
: 指定したインデックスの要素にアクセスします。
範囲チェックが行われ、無効なインデックスの場合は例外がスローされます。
operator[]
: 指定したインデックスの要素にアクセスします。
範囲チェックは行われません。
これにより、要素への安全なアクセスと高速なアクセスが可能です。
#include <iostream>
#include <deque>
int main() {
std::deque<int> dq = {10, 20, 30, 40, 50};
std::cout << "at(2): " << dq.at(2) << std::endl; // 30
std::cout << "operator[](1): " << dq[1] << std::endl; // 20
return 0;
}
at(2): 30
operator[](1): 20
sizeとresize
size
: 現在の要素数を取得します。resize
: 要素数を指定したサイズに変更します。
新しいサイズが現在のサイズより大きい場合は、追加された要素はデフォルト値で初期化されます。
これにより、std::deque
のサイズを動的に管理できます。
#include <iostream>
#include <deque>
int main() {
std::deque<int> dq = {1, 2, 3};
std::cout << "初期サイズ: " << dq.size() << std::endl; // 3
dq.resize(5); // サイズを5に変更
std::cout << "サイズ変更後: " << dq.size() << std::endl; // 5
for (const auto& elem : dq) {
std::cout << elem << " "; // 1 2 3 0 0
}
std::cout << std::endl;
return 0;
}
初期サイズ: 3
サイズ変更後: 5
1 2 3 0 0
clearとerase
clear
: 全ての要素を削除します。
サイズは0になります。
erase
: 指定した位置または範囲の要素を削除します。
これにより、std::deque
の要素を柔軟に管理できます。
#include <iostream>
#include <deque>
int main() {
std::deque<int> dq = {1, 2, 3, 4, 5};
dq.erase(dq.begin() + 1); // インデックス1の要素を削除
std::cout << "erase後: ";
for (const auto& elem : dq) {
std::cout << elem << " "; // 1 3 4 5
}
std::cout << std::endl;
dq.clear(); // 全要素を削除
std::cout << "クリア後のサイズ: " << dq.size() << std::endl; // 0
return 0;
}
erase後: 1 3 4 5
クリア後のサイズ: 0
std::dequeのイテレータ
std::deque
は、イテレータを使用して要素にアクセスすることができます。
イテレータを使うことで、コンテナ内の要素を簡単に操作したり、ループ処理を行ったりすることが可能です。
以下では、std::deque
のイテレータについて詳しく解説します。
イテレータの基本
イテレータは、コンテナ内の要素を指し示すオブジェクトで、ポインタのように振る舞います。
std::deque
のイテレータは、以下の特徴を持っています。
- ランダムアクセス: イテレータを使って、任意の位置の要素にアクセスできます。
- イテレータの種類:
std::deque
は、通常のイテレータと逆イテレータの両方を提供します。
beginとend
begin
: コンテナの最初の要素を指すイテレータを返します。end
: コンテナの最後の要素の次を指すイテレータを返します。
このため、end
は実際の要素を指し示すわけではありません。
これらのメソッドを使用することで、std::deque
の全要素に対してループ処理を行うことができます。
#include <iostream>
#include <deque>
int main() {
std::deque<int> dq = {10, 20, 30, 40, 50};
std::cout << "要素: ";
for (auto it = dq.begin(); it != dq.end(); ++it) {
std::cout << *it << " "; // 10 20 30 40 50
}
std::cout << std::endl;
return 0;
}
要素: 10 20 30 40 50
rbeginとrend
rbegin
: コンテナの最後の要素を指す逆イテレータを返します。rend
: コンテナの最初の要素の前を指す逆イテレータを返します。
このため、rend
も実際の要素を指し示すわけではありません。
逆イテレータを使用することで、std::deque
の要素を逆順に処理することができます。
#include <iostream>
#include <deque>
int main() {
std::deque<int> dq = {10, 20, 30, 40, 50};
std::cout << "逆順の要素: ";
for (auto it = dq.rbegin(); it != dq.rend(); ++it) {
std::cout << *it << " "; // 50 40 30 20 10
}
std::cout << std::endl;
return 0;
}
逆順の要素: 50 40 30 20 10
std::dequeの応用例
std::deque
は、その特性を活かしてさまざまなデータ構造として利用できます。
以下では、std::deque
をキュー、スタック、双方向リストとして使用する方法について解説します。
キューとしての使用
std::deque
は、両端からの要素の追加と削除が可能なため、キュー(FIFO:先入れ先出し)として非常に適しています。
push_back
で要素を追加し、pop_front
で要素を削除することで、キューの動作を実現できます。
#include <iostream>
#include <deque>
int main() {
std::deque<int> queue;
// キューに要素を追加
queue.push_back(1);
queue.push_back(2);
queue.push_back(3);
// キューから要素を削除
while (!queue.empty()) {
std::cout << "キューから取り出した要素: " << queue.front() << std::endl; // 1, 2, 3
queue.pop_front();
}
return 0;
}
キューから取り出した要素: 1
キューから取り出した要素: 2
キューから取り出した要素: 3
スタックとしての使用
std::deque
は、スタック(LIFO:後入れ先出し)としても使用できます。
push_back
で要素を追加し、pop_back
で要素を削除することで、スタックの動作を実現できます。
#include <iostream>
#include <deque>
int main() {
std::deque<int> stack;
// スタックに要素を追加
stack.push_back(1);
stack.push_back(2);
stack.push_back(3);
// スタックから要素を削除
while (!stack.empty()) {
std::cout << "スタックから取り出した要素: " << stack.back() << std::endl; // 3, 2, 1
stack.pop_back();
}
return 0;
}
スタックから取り出した要素: 3
スタックから取り出した要素: 2
スタックから取り出した要素: 1
双方向リストとしての使用
std::deque
は、双方向リストのように要素を前後に追加・削除できるため、双方向リストとしても利用できます。
push_front
やpush_back
を使って要素を追加し、pop_front
やpop_back
で要素を削除することで、双方向リストの機能を実現できます。
#include <iostream>
#include <deque>
int main() {
std::deque<std::string> list;
// 双方向リストに要素を追加
list.push_back("要素1");
list.push_front("要素0");
list.push_back("要素2");
std::cout << "双方向リストの要素: ";
for (const auto& elem : list) {
std::cout << elem << " "; // 要素0 要素1 要素2
}
std::cout << std::endl;
// 要素を削除
list.pop_front(); // 要素0を削除
list.pop_back(); // 要素2を削除
std::cout << "削除後の要素: ";
for (const auto& elem : list) {
std::cout << elem << " "; // 要素1
}
std::cout << std::endl;
return 0;
}
双方向リストの要素: 要素0 要素1 要素2
削除後の要素: 要素1
よくある質問
まとめ
この記事では、std::deque
の基本概念から、基本操作、メソッド、応用例、よくある質問まで幅広く解説しました。
std::deque
は、両端からの操作が可能な柔軟なコンテナであり、キューやスタック、双方向リストとしての利用が可能です。
ぜひ、実際のプログラムでstd::deque
を活用し、その特性を体験してみてください。