queue と priority_queue (使用法と模擬実装を理解する)
キューとは何ですか
- キューは、要素
FIFO
がコンテナーの一方の端から挿入され、もう一方の端から要素が抽出されるコンテキスト (先入れ先出し) で動作するように設計されたコンテナー アダプターです。 - キューはコンテナ アダプタとして実装されます。コンテナ アダプタは、特定のコンテナ クラスをその基礎となるコンテナ クラスとしてカプセル化し、
queue
その要素にアクセスするための特定のメンバ関数のセットを提供します。要素は末尾からキューに入り、先頭からデキューされます。 - 基礎となるコンテナーは、標準のコンテナー クラス テンプレートの 1 つ、または特別に設計された別のコンテナー クラスです。基礎となるコンテナは、少なくとも次の操作をサポートする必要があります。
empty : キューが空かどうかを確認します
size : キュー内の有効な要素の数を返します
Front : キューの先頭にある要素の参照を返します
back : キューの最後にある要素の参照を返します
Push_back : enterキューの最後尾のキュー
Pop_front : キューの先頭でキューを終了します。
- 標準コンテナ クラスは
deque
合計でlist
これらの要件を満たします。デフォルトでは、queue
インスタンス化にコンテナ クラスが指定されていない場合は、標準コンテナが使用されますdeque
。
キューの使用
1.キューコンストラクター
初期化 (1): 既存のコンテナでキューを初期化します。
explicit queue(const container_type& ctnr);
このコンストラクターは既存のコンテナーを取得しctnr
、その内容でキューを初期化します。
例:
std::deque<int> myDeque = {
1, 2, 3};
std::queue<int> myQueue(myDeque); // 使用已存在的 deque 初始化队列
move-initialize (2): 既存のコンテナを使用してキューを初期化し、初期化後に元のコンテナを空にします。
explicit queue(container_type&& ctnr = container_type());
このコンストラクターは、右辺値参照のコンテナーを受け取りctnr
、それを使用してキューを初期化し、初期化後にコンテナーを空にしますctnr
。
例:
std::deque<int> myDeque = {
1, 2, 3};
std::queue<int> myQueue(std::move(myDeque)); // 使用已存在的 deque 初始化队列,并清空 myDeque
アロケータ (3): カスタム アロケータ Alloc を使用してキューを初期化します。
template <class Alloc> explicit queue(const Alloc& alloc);
このコンストラクターは、初期化時にメモリを割り当てるために使用されるカスタム アロケーター タイプを受け入れますAlloc
。
例:
std::allocator<int> myAllocator;
std::queue<int> myQueue(myAllocator); // 使用自定义分配器初始化队列
init + allocator (4): 既存のコンテナーとカスタム アロケーターを使用してキューを初期化します。
template <class Alloc> queue(const container_type& ctnr, const Alloc& alloc);
このコンストラクターは、既存のコンテナーctnr
とカスタム アロケーター を受け入れますalloc
。これは、初期化時に基になるコンテナーとアロケーターを指定するために使用されます。
例:
std::deque<int> myDeque = {
1, 2, 3};
std::allocator<int> myAllocator;
std::queue<int> myQueue(myDeque, myAllocator); // 使用已存在的 deque 和自定义分配器初始化队列
move-init + allocator (5): 既存のコンテナーとカスタム アロケーターを使用してキューを初期化し、初期化後に元のコンテナーを空にします。
template <class Alloc> queue(container_type&& ctnr, const Alloc& alloc);
(2) と同様に、このコンストラクターは、右辺値参照のコンテナーと、キューの初期化に使用され、初期化後に空になるctnr
カスタム アロケーターを受け入れます。alloc
ctnr
例:
std::deque<int> myDeque = {
1, 2, 3};
std::allocator<int> myAllocator;
std::queue<int> myQueue(std::move(myDeque), myAllocator); // 使用已存在的 deque 和自定义分配器初始化队列,并清空 myDeque
copy + allocator (6): 既存のキューをコピーし、カスタム アロケータを使用して新しいキューを初期化します。
template <class Alloc> queue(const queue& x, const Alloc& alloc);
このコンストラクターは、初期化時に要素を新しいキューにコピーするための既存のキューx
とカスタム アロケーターを受け入れます。alloc
x
例:
std::queue<int> originalQueue;
// 添加一些元素到 originalQueue
std::allocator<int> myAllocator;
std::queue<int> myQueue(originalQueue, myAllocator); // 复制已存在队列并使用自定义分配器初始化新队列
move + allocator (7): 既存のキューを移動し、カスタム アロケータを使用して新しいキューを初期化します。
template <class Alloc> queue(queue&& x, const Alloc& alloc);
(5) と同様に、このコンストラクターは、右辺値参照のキューと、新しいキューを初期化しそこから要素を移動するためのx
カスタム アロケーターを受け入れます。alloc
x
例:
std::queue<int> originalQueue;
// 添加一些元素到 originalQueue
std::allocator<int> myAllocator;
std::queue<int> myQueue(std::move(originalQueue), myAllocator); // 移动已存在队列并使用自定义分配器初始化新队列
2.空()
bool empty() const
クラスのメンバー関数のstd::queue
1 つで、キューが空かどうかを判断するために使用されます。この関数はキューの内容を変更しないため、 として宣言されconst
、オブジェクトを変更しないことを示します。
戻り値:
キューが空の場合に戻りますtrue
。
キューが空でない場合に戻りますfalse
。
使用例:
#include <iostream>
#include <queue>
int main() {
std::queue<int> myQueue;
if (myQueue.empty()) {
std::cout << "Queue is empty." << std::endl;
} else {
std::cout << "Queue is not empty." << std::endl;
}
myQueue.push(42);
if (myQueue.empty()) {
std::cout << "Queue is empty." << std::endl;
} else {
std::cout << "Queue is not empty." << std::endl;
}
return 0;
}
この例では、最初に空のキューが作成されmyQueue
、次にempty()
関数を使用してキューが空かどうかが判断されます。要素を追加した後、empty()
関数を再度呼び出してキューの状態を確認します。出力から、キューに要素がない場合は空であり、要素を追加した後も空ではないことがわかります。
3.サイズ()
size_type size() const
std::queue
クラスのメンバー関数の 1 つで、キュー内の要素の数を取得するために使用されます。この関数はキューの内容を変更しないため、 として宣言されconst
、オブジェクトを変更しないことを示します。
戻り値:
キュー内の現在の要素数 (サイズ) を返します。
使用例:
#include <iostream>
#include <queue>
int main() {
std::queue<int> myQueue;
std::cout << "Initial size: " << myQueue.size() << std::endl;
myQueue.push(42);
myQueue.push(20);
myQueue.push(10);
std::cout << "Updated size: " << myQueue.size() << std::endl;
return 0;
}
この例では、最初に空のキューを作成しmyQueue
、次にsize()
関数を使用してキューの初期サイズを取得します。次に、キューに 3 つの要素を追加し、size()
関数を再度呼び出して更新されたキュー サイズを取得します。出力から、要素を追加した後にキューのサイズが変更されたことがわかります。
4.フロント()
reference& front()
これは、キューの先頭 (先頭) 要素にアクセスするために使用されるクラスのメンバー関数の 1 つですconst_reference& front() const
。std::queue
これらの関数はキューの内容を変更しないため、const
オブジェクトを変更しないことを示す定数参照として宣言されるか、定数参照を返します。
reference
と はconst_reference
テンプレートT
パラメーターの参照型と定数参照型で、それぞれキュー内の要素の型と定数参照型を表すために使用されます。返された参照により、キューの最初の要素にアクセスできるようになります。
使用例:
#include <iostream>
#include <queue>
int main() {
std::queue<int> myQueue;
myQueue.push(42);
myQueue.push(20);
int& firstElement = myQueue.front();
const int& constFirstElement = myQueue.front();
std::cout << "First element: " << firstElement << std::endl;
std::cout << "Constant first element: " << constFirstElement << std::endl;
return 0;
}
この例では、最初にキューが作成されmyQueue
、次に 2 つの要素が追加されます。関数を介してfront()
キューの最初の要素を取得し、非 const 参照firstElement
と const 参照に格納しますconstFirstElement
。出力から、両方の参照がキューの最初の要素の値にアクセスできることがわかります。
5.back();
reference& back()
これは、キューの最後の (末尾) 要素にアクセスするために使用されるクラスのメンバー関数の 1 つconst_reference& back() const
です。std::queue
これらの関数はキューの内容を変更しないため、const
オブジェクトを変更しないことを示す定数参照として宣言されるか、定数参照を返します。
reference
と はconst_reference
テンプレートT
パラメーターの参照型と定数参照型で、それぞれキュー内の要素の型と定数参照型を表すために使用されます。返された参照を使用すると、キューの最後の要素にアクセスできます。
使用例:
#include <iostream>
#include <queue>
int main() {
std::queue<int> myQueue;
myQueue.push(42);
myQueue.push(20);
int& lastElement = myQueue.back();
const int& constLastElement = myQueue.back();
std::cout << "Last element: " << lastElement << std::endl;
std::cout << "Constant last element: " << constLastElement << std::endl;
return 0;
}
この例では、最初にキューが作成されmyQueue
、次に 2 つの要素が追加されます。関数を介してキューの最後の要素を取得しback()
、非 const 参照lastElement
と const 参照に格納しますconstLastElement
。出力から、両方の参照がキューの最後の要素の値にアクセスできることがわかります。
6.プッシュ
void push (const value_type& val)
std::queue クラスのメンバー関数の 1 つでありvoid push (value_type&& val)
、キューの最後に要素を追加するために使用されます。
const value_type& val
キューに追加する必要がある要素を渡すために使用される定数参照です。
value_type&& val
キューに追加する必要がある要素を渡すために使用される右辺値参照であり、通常は移動セマンティクスをサポートするために使用されます。
使用例:
#include <iostream>
#include <queue>
int main() {
std::queue<int> myQueue;
myQueue.push(42); // 使用右值
int value = 20;
myQueue.push(value); // 使用左值
return 0;
}
この例では、最初にキューが作成されmyQueue
、次にpush()
関数を使用して異なるタイプの 2 つの値がキューに追加されます。1 つ目はpush()
rvalue を使用し42
、2 つ目はpush()
lvalue 変数を使用しますvalue
。キューには要素が追加された順序で保持されます。
7.場所
template <class... Args> void emplace (Args&&... args)
std::queue
コンストラクター パラメーター リストを介してキューの最後に新しい要素を直接構築するために使用されるクラスのメンバー関数の 1 つです。
Args... args
コンストラクターに渡されるパラメーターのリストを表すテンプレート パラメーター パックです。
この関数を使用するとemplace()
、余分なオブジェクトのコピーまたは移動操作を回避し、コンテナ内にオブジェクトを直接構築できます。
使用例:
#include <iostream>
#include <queue>
class MyObject {
public:
MyObject(int value) : m_value(value) {
std::cout << "Constructed: " << m_value << std::endl;
}
~MyObject() {
std::cout << "Destructed: " << m_value << std::endl;
}
private:
int m_value;
};
int main() {
std::queue<MyObject> myQueue;
myQueue.emplace(42); // 使用右值参数构造
myQueue.emplace(20); // 使用右值参数构造
return 0;
}
この例では、最初にキューが作成されmyQueue
、次にemplace()
関数を使用して、右辺値パラメータを介してキューの最後に 2 つのオブジェクトが直接構築されますMyObject
。を使用するためemplace()
、追加のコピーまたは移動操作が発生することなく、オブジェクトはキュー上に直接構築されます。
8.pop()
void pop()
クラスのメンバー関数のstd::queue
1 つで、キューの先頭にある要素をキューから削除するために使用されます。
この関数を呼び出すと、pop()
キュー内の最初の要素が削除され、キュー内の残りの要素が前方に移動されて、削除された要素の場所が埋められます。
使用例:
#include <iostream>
#include <queue>
int main() {
std::queue<int> myQueue;
myQueue.push(42);
myQueue.push(20);
myQueue.push(10);
std::cout << "Size before pop: " << myQueue.size() << std::endl;
myQueue.pop();
std::cout << "Size after pop: " << myQueue.size() << std::endl;
return 0;
}
この例では、最初に queue を作成しmyQueue
、次にpush()
関数を使用して 3 つの要素をキューに追加します。この関数を呼び出すとpop()
、キューの先頭の要素が42
削除されます。出力から、pop()
を呼び出した後にキューのサイズが減少していることがわかります。
9.スワップ
void swap(queue& x) noexcept
std::queue
クラスのメンバー関数の 1 つで、現在のキューと別のキューの内容を交換するために使用されますx
。
x
: 現在のキューと交換する別のキュー。
noexcept
: この関数は例外をスローしないように宣言されています。これは、交換中に例外がスローされないことを意味します。
使用例:
#include <iostream>
#include <queue>
int main() {
std::queue<int> queue1;
std::queue<int> queue2;
queue1.push(42);
queue1.push(20);
queue2.push(10);
std::cout << "Before swap:" << std::endl;
std::cout << "Queue 1 front: " << queue1.front() << std::endl;
std::cout << "Queue 2 front: " << queue2.front() << std::endl;
queue1.swap(queue2);
std::cout << "After swap:" << std::endl;
std::cout << "Queue 1 front: " << queue1.front() << std::endl;
std::cout << "Queue 2 front: " << queue2.front() << std::endl;
return 0;
}
この例では、まず 2 つのキューを作成しqueue1
、queue2
それぞれに要素を追加します。この関数を呼び出すとswap()
、キューの内容が交換されます。出力から、スワップ後にキューの内容もスワップされたことがわかります。
キューシミュレーションの実装
#pragma once
#include <deque>
namespace xzq
{
template<class T, class Container = deque<T>>
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;
};
}
まず、このキュー クラスは、Container
デフォルト値 の で呼び出されるテンプレート パラメータを受け取りますstd::deque<T>
。これにより、キュー オブジェクトの作成時に基になるコンテナ タイプを指定できます。指定しない場合は、デフォルトが使用されますstd::deque
。
push(const T& x)
x
関数はキューの最後に要素を追加するために使用され、実際にはpush_back()
基礎となるコンテナーのメソッドを呼び出します。
pop()
この関数はキューの先頭にある要素を削除するために使用され、実際にはpop_front()
基になるコンテナーのメソッドを呼び出します。
back()
この関数はキュー内の最後の要素への参照を返します。これを使用してキューの最後の要素にアクセスできます。
front()
この関数は、キューの最初の要素への参照を返します。これは、キューの先頭要素にアクセスするために使用されます。
empty()
キューが空かどうかをチェックする関数ですが、実際にはempty()
基になるコンテナのメソッドを呼び出します。
size()
この関数はキュー内の要素の数を取得するために使用され、実際にはsize()
基になるコンテナーのメソッドを呼び出します。
プライベート メンバーは、_con
キューの要素を格納するために使用される基礎となるコンテナ オブジェクトです。
このシミュレートされたキュー クラスを使用すると、基になるさまざまなコンテナ タイプ (デフォルトはstd::deque
) を選択し、クラスのメソッドを呼び出して、要素の追加、要素の削除、要素へのアクセス、要素が存在するかどうかの判断など、キューの基本操作をシミュレートできます。空など この実装により、テンプレートを通じてキューをより柔軟に使用し、さまざまなデータ型や基礎となるコンテナーに適応できるようになります。
priority_queueとは何ですか
- 優先キューは、厳密な弱い順序付け基準に従って、最初の要素が常にそれに含まれる要素の最大であるコンテナ アダプタです。
- このコンテキストはヒープに似ており、いつでも要素を挿入でき、最大のヒープ要素 (優先キューの先頭にある要素) のみを取得できます。
- 優先キューは、コンテナ アダプタとして実装されます。コンテナ アダプタは、特定のコンテナ クラスをその基礎となるコンテナ クラスとしてカプセル化し、
queue
その要素にアクセスするための特定のメンバ関数のセットを提供します。要素は、優先キューの先頭と呼ばれる特定のコンテナの「末尾」からポップされます。 - 基礎となるコンテナーには、標準のコンテナー クラス テンプレート、または特定の設計の他のコンテナー クラスを使用できます。コンテナーはランダム アクセス イテレーターを介してアクセス可能であり、次の操作をサポートする必要があります。
empty() : コンテナが空かどうかを確認します
size() : コンテナ内の有効な要素の数を返します
Front() : コンテナ内の最初の要素への参照を返します
Push_back() : コンテナの最後に要素を挿入します
Pop_back() : コンテナの末尾の要素を削除します
- 標準のコンテナ クラス
vector
で、deque
これらのニーズを満たします。デフォルトでは、priority_queue
特定のクラスのインスタンス化にコンテナ クラスが指定されていない場合、それが使用されますvector
。 - ヒープ構造が常に内部的に維持されるように、ランダム アクセス反復子をサポートする必要があります。コンテナ アダプタは、必要に応じてアルゴリズム関数を自動的に呼び出すことにより
make_heap
、これを自動的に実行します。push_heap
pop_heap
priority_queue の使用
1. priority_queue コンストラクター
std::priority_queue
は、 によって提供される優先キュー コンテナですC++ STL
。これはヒープに基づいて実装されており、特定の順序で要素を追加および削除できます。
これらのコンストラクターの説明と例を次に示します。
priority_queue(const Compare& comp, const Container& ctnr)
comp
: 指定された比較関数と基礎となるコンテナーを使用して、優先キューを構築しますctnr
。
priority_queue(InputIterator first, InputIterator last, const Compare& comp, const Container& ctnr)
[first, last)
:指定された比較関数comp
と基礎となるコンテナーを備えた反復子の範囲内の要素を使用して、優先キューを構築しますctnr
。
explicit priority_queue(const Compare& comp = Compare(), Container&& ctnr = Container())
comp
: 指定された比較関数と、移動セマンティクスで渡された基礎となるコンテナーを使用して、優先キューを構築しますctnr
。
template <class InputIterator> priority_queue(InputIterator first, InputIterator last, const Compare& comp, Container&& ctnr = Container())
[first, last)
: イテレータ範囲内の要素と、指定された比較関数および移動セマンティクスでcomp
渡された基礎となるコンテナを使用して、優先キューを構築しますctnr
。
Allocator
バージョン: これらのコンストラクターは、異なるアロケーターを使用してallocator
優先キューを構築します。
例:
#include <iostream>
#include <queue>
#include <vector>
int main() {
// 使用默认底层容器 std::vector,以默认比较函数(最大堆)构造优先队列
std::priority_queue<int> maxHeap;
// 使用自定义比较函数(最小堆)和底层容器 std::deque 构造优先队列
auto compare = [](int a, int b) {
return a > b; };
std::deque<int> container = {
5, 3, 8, 1, 9};
std::priority_queue<int, std::deque<int>, decltype(compare)> minHeap(compare, container);
// 使用迭代器范围构造优先队列
std::vector<int> elements = {
7, 2, 4, 6, 0};
std::priority_queue<int, std::vector<int>> iteratorQueue(elements.begin(), elements.end());
// 输出优先队列中的元素
while (!iteratorQueue.empty()) {
std::cout << iteratorQueue.top() << " ";
iteratorQueue.pop();
}
return 0;
}
この例では、さまざまなコンストラクターの使用法を示します。まず、デフォルトのコンストラクターを使用して最大ヒープ優先キューが構築されます。次に、カスタム比較関数と基礎となるコンテナーを使用して、std::deque
最小ヒープ優先キューを構築します。最後に、反復子の範囲を使用して優先キューが構築されます。出力に基づいて、プライオリティ キューが異なる順序で要素を出力していることがわかります。
1.1 テンプレートパラメータの比較
クラスでは、要素の比較に使用される関数オブジェクトがstd::priority_queue
テンプレート パラメーターを通じて指定され、ヒープの並べ替え方法に影響を与えます。ですCompare
Compare
ファンクタ、要素の比較方法を定義します。優先順位に応じて、Compare
キューは大きなヒープ (最大ヒープ) または小さなヒープ (最小ヒープ) になります。
デフォルトstd::less<T>
(大規模ヒープ):
std::less は、2 つの要素を比較するためにoperator() をオーバーロードする関数オブジェクトです。最初の引数が 2 番目の引数より小さいかどうかを示すブール値を返します。デフォルトでは、Compare パラメーターが指定されていない場合、プライオリティ キューは比較関数オブジェクトとして std::less、つまり大きなヒープを使用します。これは、大きなヒープでは、親ノードの値が常に子ノードの値以上であることを意味します。
std::greater<T>
(小規模ヒープ):
std::greater<T>
別の関数オブジェクトであり、オーバーロードされoperator()
、2 つの要素を比較するために使用されます。とは異なりstd::less<T>
、std::greater<T>
最初の引数が 2 番目の引数より大きいかどうかを示すブール値を返します。std::greater<T>
に渡すとpriority_queue
、小さなヒープが構築されます。小さなヒープでは、親ノードの値は常に子ノードの値以下になります。
以下は、さまざまな比較関数オブジェクトを使用して大規模なヒープと小規模なヒープを作成する方法を示すサンプル コードです。
#include <iostream>
#include <queue>
int main() {
std::priority_queue<int> maxHeap; // 默认大堆
std::priority_queue<int, std::vector<int>, std::greater<int>> minHeap; // 小堆
maxHeap.push(5);
maxHeap.push(3);
maxHeap.push(8);
minHeap.push(5);
minHeap.push(3);
minHeap.push(8);
std::cout << "Max Heap (Top element): " << maxHeap.top() << std::endl;
std::cout << "Min Heap (Top element): " << minHeap.top() << std::endl;
return 0;
}
この例では、大きなヒープと小さなヒープを別々に作成します。この関数によりtop()
、大きな杭の一番上の要素が最大であり、小さな杭の一番上の要素が最も小さいことがわかります。これは、さまざまな比較関数オブジェクトの影響を反映しています。
1.2 ファンクターとは何ですか?
Functor はoperator()
、オブジェクトを関数のように呼び出せるように、関数呼び出し演算子をオーバーロードするクラス オブジェクトです。これは実際には関数オブジェクトであり、独自のメンバー変数と演算を持つことができ、通常の関数と同様に使用できます。
ファンクターを使用する主な利点の 1 つは、関数の動作と状態をオブジェクトにカプセル化できるため、コードが読みやすく、保守しやすくなることです。STL
ファンクターは、標準アルゴリズム、コンテナー、関数操作が必要なその他の場所など、さまざまな状況で使用できます。
ファンクター クラスには通常、少なくとも実装が必要ですoperator()
。これは、ファンクターの目的に応じて、異なるパラメーターと戻り値の型を持つことができます。簡単な例を次に示します。
#include <iostream>
class Adder {
public:
Adder(int value) : value_(value) {
}
int operator()(int x) {
return x + value_;
}
private:
int value_;
};
int main() {
Adder addFive(5);
Adder addTen(10);
int result1 = addFive(7); // 调用仿函数 addFive
int result2 = addTen(7); // 调用仿函数 addTen
std::cout << "Result 1: " << result1 << std::endl;
std::cout << "Result 2: " << result2 << std::endl;
return 0;
}
この例では、Adder
クラスは整数値を受け取り、呼び出されたときに渡された引数にその値を追加するファンクターとして定義されています。この関数では2 つのオブジェクトmain()
を作成し、それらを呼び出して加算演算を実行します。Adder
addFive
addTen
つまり、ファンクターは、オブジェクトを関数のように呼び出すことを可能にする関数オブジェクトであり、関数の動作と状態をオブジェクトにカプセル化できます。これは、より柔軟で読みやすいコードを作成する場合に非常に役立ちます。
2.空()
bool empty() const
std::priority_queue
クラスのメンバー関数の 1 つで、優先キューが空かどうかを確認するために使用されます。
empty()
この関数は、優先キューが空かどうかを示すブール値を返します。
const
修飾子は、この関数が優先キューの内容を変更しないことを示します。
使用例:
#include <iostream>
#include <queue>
int main() {
std::priority_queue<int> maxHeap;
if (maxHeap.empty()) {
std::cout << "The priority queue is empty." << std::endl;
} else {
std::cout << "The priority queue is not empty." << std::endl;
}
maxHeap.push(42);
if (maxHeap.empty()) {
std::cout << "The priority queue is empty." << std::endl;
} else {
std::cout << "The priority queue is not empty." << std::endl;
}
return 0;
}
この例では、最初に空のstd::priority_queue
オブジェクトを作成しmaxHeap
、次にempty()
関数を使用してそれが空かどうかを確認します。出力から、要素を追加した後、優先キューが空ではなくなっていることがわかります。
3.サイズ()
size_type size() const
std::priority_queue
クラスのメンバー関数の 1 つで、優先キュー内の要素の数を取得するために使用されます。
size()
size_type
この関数は、優先キュー内の要素の数を表す符号なし整数を返します。
const
修飾子は、この関数が優先キューの内容を変更しないことを示します。
使用例:
#include <iostream>
#include <queue>
int main() {
std::priority_queue<int> maxHeap;
maxHeap.push(42);
maxHeap.push(20);
maxHeap.push(10);
std::cout << "Size of the priority queue: " << maxHeap.size() << std::endl;
return 0;
}
この例では、3 つの要素を持つ大きなヒープ優先キューを作成しmaxHeap
、size()
関数を使用してキュー内の要素の数を取得します。出力から、キュー内に 3 つの要素があることがわかります。
4.トップ()
const_reference top() const
std::priority_queue
クラスのメンバー関数の 1 つで、優先キューの最上位要素 (ヒープのタイプに応じて最大の要素または最小の要素) を取得するために使用されますが、キューの内容は変更されません。
top()
この関数は、 queue の最上位要素const_reference
(最大の要素または最小の要素) への定数参照を返します。
const
修飾子は、この関数が優先キューの内容を変更しないことを示します。
使用例:
#include <iostream>
#include <queue>
int main() {
std::priority_queue<int> maxHeap;
maxHeap.push(42);
maxHeap.push(20);
maxHeap.push(10);
std::cout << "Top element: " << maxHeap.top() << std::endl;
return 0;
}
この例では、大きなヒープ優先キューを作成しmaxHeap
、3 つの要素を追加します。この関数を使用してtop()
、キュー内の先頭の要素 (最大の要素) を取得します。出力によると、最上位要素の値が であることがわかります42
。
5.プッシュ
void push(const value_type& val)
これは、優先キューに新しい要素を追加するために使用されるクラスのメンバー関数の 1 つvoid push(value_type&& val)
です。std::priority_queue
push(const value_type& val)
定数参照を取得しval
、新しい要素を優先キューにコピーします。
push(value_type&& val)
右辺値参照を取得しval
、移動セマンティクスを使用して新しい要素を優先キューに移動します。
使用例:
#include <iostream>
#include <queue>
int main() {
std::priority_queue<int> maxHeap;
maxHeap.push(42); // 使用 push(const value_type& val)
maxHeap.push(20); // 使用 push(const value_type& val)
maxHeap.push(10); // 使用 push(const value_type& val)
std::cout << "Top element: " << maxHeap.top() << std::endl;
return 0;
}
この例では、2 つの異なる方法を使用してバルク優先キューに要素を追加しますmaxHeap
。1 つ目は を使用することpush(const value_type& val)
、2 つ目は を使用することですpush(value_type&& val)
。どちらの方法でも要素をキューに追加します。42
出力によると、最上位要素の値は、優先キューが自動的に最大 (または最小) の要素を最上位に配置するためであることがわかります。
6.場所
template <class... Args> void emplace(Args&&... args)
クラスのメンバー関数のstd::priority_queue
1 つで、インプレース構築によって優先キューに新しい要素を挿入するために使用されます。
emplace()
関数はパラメーター パックを使用してparameter pack
、要素の構築に必要なパラメーターを受け取ります。
追加のコピーまたは移動操作を回避しながら、既存の要素に挿入できるため、効率が向上します。
この関数は、完全転送を使用して引数を渡し、さまざまなタイプのコンストラクターに対応します。
使用例:
#include <iostream>
#include <queue>
int main() {
std::priority_queue<int> maxHeap;
maxHeap.emplace(42); // 插入一个新元素
int value = 20;
maxHeap.emplace(value); // 插入一个新元素,使用拷贝构造函数
maxHeap.emplace(10); // 插入一个新元素
std::cout << "Top element: " << maxHeap.top() << std::endl;
return 0;
}
この例では、emplace()
関数を使用して、インプレース構築で新しい要素をバルク優先キューに挿入しますmaxHeap
。挿入中に、要素の構築に必要なパラメータを渡すことができます。この関数は、適切なコンストラクターを自動的に呼び出して、新しい要素をヒープに挿入します。出力によると、最上位要素の値が であることがわかります42
。
7.pop()
void pop()
std::priority_queue
優先キューの最上位要素 (ヒープのタイプに応じて最大の要素または最小の要素) を削除するクラスのメンバー関数の 1 つです。
pop()
この関数は、ヒープのプロパティを維持するためにヒープを再調整しながら、優先キューの最上位要素を削除します。
この関数を呼び出す前にキューが空でないことを確認する必要があることに注意してください。空でない場合、未定義の動作が発生します。
使用例:
#include <iostream>
#include <queue>
int main() {
std::priority_queue<int> maxHeap;
maxHeap.push(42);
maxHeap.push(20);
maxHeap.push(10);
std::cout << "Top element before pop: " << maxHeap.top() << std::endl;
maxHeap.pop();
std::cout << "Top element after pop: " << maxHeap.top() << std::endl;
return 0;
}
この例では、まず大きなヒープ優先キューを作成しmaxHeap
、次にpush()
関数を使用して 3 つの要素を追加します。この関数を使用してtop()
、キューの先頭の要素を取得します。次に、pop()
関数を使用して最上位の要素を削除し、top()
関数を通じて新しい最上位の要素を再度取得します。出力から、要素を削除すると、キューの最上位要素が になることがわかります20
。
8.スワップ
void swap(priority_queue& x) noexcept
std::priority_queue
クラスのメンバー関数の 1 つで、2 つの優先キューの内容を交換するために使用されます。
swap()
この関数は、呼び出し元のオブジェクトと渡されたパラメーター x の間でコンテンツを交換するために使用されます。
この操作により、2 つの優先キューの内容が交換されますが、それらの比較関数やその他のプロパティは変更されません。
noexcept
キーワードは、この関数が例外をスローしないことを示します。
使用例:
#include <iostream>
#include <queue>
int main() {
std::priority_queue<int> maxHeap1;
std::priority_queue<int> maxHeap2;
maxHeap1.push(42);
maxHeap1.push(20);
maxHeap2.push(10);
maxHeap2.push(30);
std::cout << "Max Heap 1 (Top element before swap): " << maxHeap1.top() << std::endl;
std::cout << "Max Heap 2 (Top element before swap): " << maxHeap2.top() << std::endl;
maxHeap1.swap(maxHeap2);
std::cout << "Max Heap 1 (Top element after swap): " << maxHeap1.top() << std::endl;
std::cout << "Max Heap 2 (Top element after swap): " << maxHeap2.top() << std::endl;
return 0;
}
maxHeap1
この例では、優先キューと の2 つの大きなヒープを作成しmaxHeap2
、それぞれに異なる要素を追加します。この関数を使用してtop()
、2 つのキューの最上位要素を取得します。次にswap()
、この関数を使用して、2 つのキューの内容を交換します。出力から、交換後に 2 つのキューの内容が交換されたことがわかります。
priority_queue の実装をシミュレートする
#pragma once
namespace xzq
{
// Compare进行比较的仿函数 less->大堆
// Compare进行比较的仿函数 greater->小堆
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;
}
for (int i = (_con.size()-1-1)/2; i >= 0; --i)
{
adjust_down(i);
}
}
void adjust_up(size_t child)
{
Compare com;
size_t parent = (child - 1) / 2;
while (child > 0)
{
if (com(_con[parent], _con[child]))
{
std::swap(_con[child], _con[parent]);
child = parent;
parent = (child - 1) / 2;
}
else
{
break;
}
}
}
void push(const T& x)
{
_con.push_back(x);
adjust_up(_con.size() - 1);
}
void adjust_down(size_t parent)
{
Compare com;
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[child], _con[parent]);
parent = child;
child = parent * 2 + 1;
}
else
{
break;
}
}
}
void pop()
{
std::swap(_con[0], _con[_con.size() - 1]);
_con.pop_back();
adjust_down(0);
}
const T& top()
{
return _con[0];
}
bool empty() const
{
return _con.empty();
}
size_t size() const
{
return _con.size();
}
private:
Container _con;
};
}
名前空間 xzq:
このコードはxzq
、他のコードとの名前の競合を避けるために、関連するクラス、関数などをカプセル化するために使用されるカスタム名前空間である名前空間にあります。
テンプレート クラス priority_queue:
プライオリティ キューの実装を表すテンプレート クラスです。これは 3 つのテンプレート引数を取ります: T
(要素タイプ)、Container
(基礎となるコンテナタイプ、デフォルトはstd::vector<T>
)、およびCompare
(要素を比較するためのファンクタ、デフォルトはstd::less<T>
)
Compare
要素を比較するために使用されるテンプレート パラメーターです。これは、ユーザー指定の優先キューのタイプに応じてstd::less<T>
(デフォルト) または となるファンクターです。ファンクターは、ヒープ内の要素の順序を決定するために使用され、それによって最大ヒープであるか最小ヒープであるかが決定されます。クラスの各メンバ関数では、ファンクタを呼び出すことで要素の比較を行うことで、ヒープの挿入や調整の操作を実現しています。std::greater<T>
Compare
priority_queue
Compare
コンストラクター priority_queue():これは、比較にファンクターを
使用する必要のないデフォルトのコンストラクターです。Compare
コンストラクター テンプレート priority_queue(InputIterator first、InputIterator last):
コンストラクター内で、Compare
ファンクターを使用して比較演算を実行し、要素の順序を決定します。要素を追加した後、adjust_down
関数を呼び出してヒープを構築します。
メンバー関数adjust_up(size_t child):
float演算では、Compare
ファンクターを使用して比較を実行し、親ノードと子ノードの位置を交換するかどうかを決定します。これにより、ヒープの性質が維持されます。
メンバー関数 Push(const T& x):
挿入操作では、まず新しい要素を基になるコンテナーに追加し_con
、次にadjust_up
関数を呼び出してフローティング操作を実行して、新しい要素が適切な位置にあることを確認します。
メンバー関数Adjust_down(size_tparent):
シンキング操作では、Compare
ファンクターを使用して比較を実行し、親ノードと子ノードの位置を交換する必要があるかどうかを判断し、それによってヒープの性質を維持します。
メンバー関数 Pop():
削除操作では、最初に上部の要素を下部のコンテナーの最後の要素と交換し、次に、adjust_down 関数を呼び出してシンク操作を実行してヒープの性質を確認します。
メンバー関数 const T& top():
を返すことで_con[0]
優先キューの最上位要素を取得します。
エピローグ
興味のある友達は作者に注目してください、内容が良いと思うなら、ワンクリックトリプルリンクをお願いします、カニカニ!!!
作るのは簡単ではありませんので、不正確な点があればご指摘ください
ご訪問ありがとうございます、UUの視聴が私が頑張れるモチベーションになっています
時間をきっかけに、お互いより良い人間になりましょう!!!