目次
1.3.3 スタックとキューの基礎となるデフォルトのコンテナとして deque を選択する理由
4.3 priority_queue シミュレーションの実装
1. コンテナアダプター
1.1 アダプターとは
1.2 STL標準ライブラリのスタックとキューの基礎構造
1.3と
1.3.1 deque 原則の概要 (理解)
deque は反復子の助けを借りて、どのようにして仮想的な連続構造を維持するのでしょうか?
1.3.2 deque の長所と短所
deque: 1.operator[ ] の計算は少し複雑で、大量に使用されるため、パフォーマンスが低下します (ベクトルと比較して)。
2. 途中の挿入や削除の効率が高くない
3. 基礎となる角度反復子は複雑になる可能性があります
1.3.3 スタックとキューの基礎となるデフォルトのコンテナとして deque を選択する理由
1. Head-to-Tail の挿入と削除が非常に適しており、Vector や List と比較して、スタックやキューのデフォルトの適応コンテナに非常に適しています
2. 途中の多目的リストの挿入と削除
3. ランダムアクセス多目的ベクトル
2. スタックの導入と利用
2.1 スタックの概要
- stack は、特に後入れ先出し操作を使用するコンテキスト環境で使用されるコンテナ アダプタであり、その削除ではコンテナの一方の端から要素を挿入および抽出することしかできません。
- スタックはコンテナ アダプタとして実装されます。コンテナ アダプタは、特定のクラスをその基礎となるコンテナとしてカプセル化し、特定のクラスをその基礎となる要素固有のコンテナ テール(つまり、スタック) がプッシュおよびポップされます。
- スタックの基礎となるコンテナーは、任意の標準コンテナー クラス テンプレートまたはその他の特定のコンテナー クラスにすることができ、これらのコンテナー クラスは次の操作をサポートする必要があります。
empty:判定空演算
size: スタック内の有効な要素の数を返します。
back: 末尾要素の操作を取得します。
Push_back: 要素の末尾挿入操作
Pop_back: 要素の末尾削除操作
- 標準コンテナーの Vector、deque、および list はすべて、これらの要件を満たしています。スタックに対して特定の基になるコンテナーが指定されていない場合、デフォルトでは deque が使用されます。
2.2 スタックの使用
機能説明 |
インターフェースの説明
|
スタック() | 空のスタックを構築する |
空() | スタックが空かどうかを確認する |
サイズ() | スタック内の要素の数を返します。 |
上() | スタックの最上位要素への参照を返します。 |
押す() | 要素 val をスタックにプッシュします |
ポップ() | 要素をスタックの最後にポップします |
2.3 スタックシミュレーションの実装
template<class T,class Container=deque<T>>
class stack
{
public:
void push(const T& x)
{
_con.push_back(x);
}
void pop()
{
_con.pop_back();
}
T& top()
{
return _con.back();
}
const T& top() const
{
return _con.back();
}
bool empty() const
{
return _con.empty();
}
size_t size() const
{
return _con.size();
}
private:
Container _con;
};
3. キューの導入と使用
3.1 キューの概要
- キューは、要素がコンテナーの一方の端から挿入され、もう一方の端から抽出される FIFO コンテキスト (先入れ先出し) で動作するように設計されたコンテナー アダプターです。
- キューは、特定のコンテナ クラスを基になるコンテナ クラスとしてカプセル化するコンテナ アダプタとして実装され、キューはその要素にアクセスするための特定のメンバ関数のセットを提供します。要素は末尾からキューに入り、先頭からデキューされます。
- 基礎となるコンテナーは、標準のコンテナー クラス テンプレートの 1 つ、または特別に設計された別のコンテナー クラスです。基礎となるコンテナは、少なくとも次の操作をサポートする必要があります。
empty: キューが空かどうかを確認しますsize: キュー内の有効な要素の数を返します。フロント: キューの先頭要素への参照を返します。back: キューの最後にある要素への参照を返します。Push_back: キューの最後からキューに入るPop_front: キューの先頭でキューから外されます
- 標準コンテナ クラスの deque および list は、これらの要件を満たします。デフォルトでは、キューのインスタンス化にコンテナー クラスが指定されていない場合は、標準のコンテナー両端キューが使用されます。
3.2 キューの使用
関数宣言 | インターフェースの説明 |
列() | 空のキューを構築する |
空() | キューが空かどうかを確認し、true を返し、それ以外の場合は false を返します。 |
サイズ() | キュー内の有効な要素の数を返します。 |
正面() | キューの先頭にある要素への参照を返します。 |
戻る() | キューの最後にある要素への参照を返します。 |
押す() | 要素 val をキューの最後にエンキューします |
ポップ() | キューの先頭にある要素をデキューします |
3.3 キューシミュレーションの実装
template<class T,class Container=deque<int>>
class queue
{
public:
void push(const T& x)
{
_con.push_back(x);
}
void pop()
{
_con.pop_front();
}
T& back()
{
return _con.back();
}
T& front()
{
return _con.front();
}
const T& back() const
{
return _con.back();
}
const T& front() const
{
return _con.front();
}
bool empty() const
{
return _con.empty();
}
size_t size() const
{
return _con.size();
}
private:
Container _con;
};
4. 優先キュー
4.1 priority_queue の概要
ファンクター は、関数オブジェクト (Function オブジェクト) とも呼ばれ、関数関数を実行できるクラスです。
ファンクターの構文は通常の関数呼び出しとほぼ同じですが、ファンクターのクラスとして、operator() 演算子をオーバーロードする必要があります。なぜなら、ファンクターを呼び出すことは、実際にはクラスオブジェクトを通じてオーバーロードされたoperator()演算子を呼び出すことになるからです。
- 優先キューは、厳密な弱い順序付け基準に従って、最初の要素が常にそれに含まれる要素の最大であるコンテナ アダプタです。
- このコンテキストはヒープに似ており、いつでも要素を挿入でき、最大のヒープ要素 (優先キューの先頭にある要素) のみを取得できます。
- 優先キューは、特定のコンテナ クラスをその基礎となるコンテナ クラスとしてカプセル化するコンテナ アダプタとして実装され、キューはその要素にアクセスするための特定のメンバー関数のセットを提供します。要素は、優先キューの先頭と呼ばれる特定のコンテナの「末尾」からポップされます。
- 基礎となるコンテナーには、標準のコンテナー クラス テンプレート、または特定の設計の他のコンテナー クラスを使用できます。コンテナーはランダム アクセス イテレーターを介してアクセス可能であり、次の操作をサポートする必要があります。
empty(): コンテナが空かどうかを確認しますsize(): コンテナ内の有効な要素の数を返します。Front(): コンテナ内の最初の要素への参照を返します。Push_back(): コンテナの最後に要素を挿入します。Pop_back(): コンテナの末尾要素を削除します。
- 標準コンテナ クラスの Vector と deque は、これらのニーズを満たします。デフォルトでは、特定の priority_queue クラスのインスタンス化にコンテナ クラスが指定されていない場合は、vector が使用されます。
- ヒープ構造が常に内部的に維持されるように、ランダム アクセス反復子をサポートする必要があります。コンテナー アダプターは、必要に応じてアルゴリズム関数 make_heap、push_heap、pop_heap を自動的に呼び出すことで、これを自動的に行います。
4.2 priority_queue の使用
関数宣言 | インターフェースの説明 |
優先キュー() priority_queue(最初、最後) |
空の優先キューを構築する |
空() | 優先キューが空かどうかを検出し、true を返し、それ以外の場合は false を返します。 |
上() | 優先キュー内の最大 (最小) 要素、つまり最上位の要素を返します。 |
押す() | 要素 val を優先キューに挿入します |
ポップ() | 優先キュー内の最大 (最小) 要素、つまりヒープの最上位要素を削除します。 |
//构造一个空的优先队列(此优先队列默认为大顶堆)
priority_queue<int> big_heap;
//另一种构建大顶堆的方法
priority_queue<int,vector<int>,less<int> > big_heap2;
小さなトップヒープ (昇順)
//构造一个空的优先队列,此优先队列是一个小顶堆
priority_queue<int,vector<int>,greater<int> > small_heap;
知らせ:
より少ないものとより大きなものを使用する場合は、ヘッダー ファイルが必要です。
#include <functional>
カスタム タイプ データを priority_queue に配置する場合、ユーザーはカスタム タイプで > または < オーバーロードを指定する必要があります。
4.3 priority_queue シミュレーションの実装
template<class T, class Container = vector<T>,class Compare=std::less<T>>
class priority_queue
{
public:
priority_queue()
{}
template<class InputIterator>
priority_queue(InputIterator first, InputIterator last)
{
while (first != last)
{
_con.push_back(*first);
++first;
}
//建堆,(_con.size()-1-1)/2为末尾节点的父节点所在位置
for (int i = ((_con.size() - 1 - 1) / 2); i >= 0; i--)
{
//向下建堆logN
adjust_down(i);
}
}
//向上建堆
void adjust_up(size_t child)
{
/*size_t parent = (child - 1) / 2;
while (child > 0)
{
//建大堆
if (_con[parent] < _con[child])
{
std::swap(_con[parent], _con[child]);
child = parent;
parent = (child - 1) / 2;
}
else
{
break;
}
}*/
Compare com;//仿函数,类对象像函数一样使用,重载operator()
size_t parent = (child - 1) / 2;
while (child > 0)
{
//建大堆
if (com(_con[parent],_con[child]))
{
std::swap(_con[parent], _con[child]);
child = parent;
parent = (child - 1) / 2;
}
else
{
break;
}
}
}
void push(const T& x)
{
//先vector插入,再向上调整
_con.push_back(x);
adjust_up(_con.size() - 1);
}
//向下建堆
void adjust_down(size_t parent)
{
/*size_t child = parent * 2 + 1;
while (child < _con.size())
{
//选出左右孩子大的那一个
if (child + 1 < _con.size() && _con[child] < _con[child + 1])
{
++child;
}
//建大堆
if (_con[parent] < _con[child])
{
std::swap(_con[parent], _con[child]);
parent = child;
child = parent * 2 + 1;
}
else
{
break;
}
}*/
Compare com;//仿函数,类对象像函数一样使用,重载operator()
size_t child = parent * 2 + 1;
while (child < _con.size())
{
//选出左右孩子大的那一个
if (child + 1 < _con.size() && com(_con[child] , _con[child + 1]))
{
++child;
}
//建大堆
if (com(_con[parent] , _con[child]))
{
std::swap(_con[parent], _con[child]);
parent = child;
child = parent * 2 + 1;
}
else
{
break;
}
}
}
void pop()
{
//根与最右下边的孩子交换,vector尾删,再下调整
std::swap(_con[0], _con[_con.size() - 1]);
_con.pop_back();
adjust_down(0);
}
const T& top() const
{
return _con[0];
}
bool empty() const
{
return _con.empty();
}
size_t size() const
{
return _con.size();
}
private:
Container _con;
};
ここではヒープ構築プロセスについては詳しく説明しません。データ構造のコンテンツを検索して自分で確認できます。