記事ディレクトリ
説明する
1. 優先キューは、厳格な弱い順序付け基準に従って、最初の要素が含まれる要素の中で常に最大となるコンテナ アダプタです。
2. このコンテキストはヒープに似ており、いつでも要素を挿入でき、最大のヒープ要素 (優先キューの先頭の要素) のみを取得できます。
3. 優先キューは、特定のコンテナ クラスをその基礎となるコンテナ クラスとしてカプセル化するコンテナ アダプタとして実装され、キューはその要素にアクセスするための特定のメンバー関数のセットを提供します。要素は、優先キューの先頭と呼ばれる特定のコンテナの「末尾」からポップされます。
4. 基礎となるコンテナには、標準のコンテナ クラス テンプレート、またはその他の特別に設計されたコンテナ クラスを使用できます。コンテナはランダム アクセス イテレータを介してアクセス可能であり、次の操作をサポートする必要があります。
empty(): コンテナが空かどうかを確認します。
size(): コンテナ内の有効な要素の数を返します。front
(): 最初の要素への参照を返します。コンテナ内で
Push_back( ): コンテナの末尾に要素を挿入します。
Pop_back(): コンテナの末尾にある要素を削除します。
5. 標準コンテナ クラスの Vector および deque は、これらの要件を満たします。デフォルトでは、特定の priority_queue クラスのインスタンス化にコンテナ クラスが指定されていない場合は、vector が使用されます。
6. ヒープ構造が常に内部的に維持されるように、ランダム アクセス反復子をサポートする必要があります。コンテナー アダプターは、必要に応じてアルゴリズム関数 make_heap、push_heap、pop_heap を自動的に呼び出すことで、これを自動的に行います。
1. 共通インターフェース
2. シミュレーションの実装
優先キューの最下層はバイナリ ツリー ヒープで実装されているため、キューの実装はヒープとほぼ同じですが、新しい概念が導入されています。ファンクター -fun 関数の構文は、私たちの関数とほぼ同じです。通常の関数呼び出しですが、ファンクターのクラスとして、operator() 演算子をオーバーロードする必要があります。なぜなら、ファンクターを呼び出すことは、実際にはクラスオブジェクトを通じてオーバーロードされたoperator()演算子を呼び出すことになるからです。ファンクターを使用すると、汎用テンプレートをより簡単に実装できます。
//一个简单的仿函数
template<class T>
class Less
{
public:
//重载 '()'
bool operator()(const T& x,const T& y)
{
//内置类型直接比大小,自定义大小会调用他们自己的运算符重载 ‘<’ 比较大小
return x<y;
}
}
#include<iostream>
#pragma once
#include<vector>
namespace tzc
{
//此处的最后一个模板参数使用仿函数,默认调用库中的less 这样建堆默认是大堆
template<class T,class Container =vector<T>,class Compare =less<int>>
class priority_queue
{
private:
//向下调整
void AdjustDown(int parent)
{
Compare com;
//找出左右孩子大的
int 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]))
{
//孩子结点比父节点还大
swap(_con[child], _con[parent]);
parent = child;
child = parent * 2 + 1;
}
else break;
}
}
//向上调整
void AdjustUp(int child)
{
Compare com;
int parent = (child - 1) / 2;
while (child > 0)
{
//如果孩子结点比父节点大交换
if (com(_con[parent],_con[child]))
{
swap(_con[child], _con[parent]);
child = parent;
parent = (child - 1) / 2;
}
else
{
break;
}
}
}
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--)
{
AdjustDown(i);
}
}
void pop()
{
swap(_con[0], _con[_con.size() - 1]);
_con.pop_back();
AdjustDown(0);
}
void push(const T& val)
{
_con.push_back(val);
AdjustUp(_con.size() - 1);
}
const T& top()
{
return _con[0];
}
bool empty()
{
return _con.empty();
}
int size()
{
return _con.size();
}
private:
Container _con;
};
void test_priority()
{
priority_queue<int> pq;
pq.push(3);
pq.push(5);
pq.push(1);
pq.push(4);
while (!pq.empty())
{
cout << pq.top()<<" ";
pq.pop();
}
cout << endl;
}
}
2. Oj 面接でよくある質問
配列内の K 番目に大きい要素
整数配列 nums と整数 k を指定すると、配列内で k 番目に大きい要素を返します。
検索しているのは、k 番目の個別の要素ではなく、並べ替えられた配列の k 番目に大きい要素であることに注意してください。
この問題を解決するには、時間計算量 O(n) のアルゴリズムを設計して実装する必要があります。
例 1:
入力: [3,2,1,5,6,4]、k = 2
出力: 5
例 2:
入力: [3,2,3,1,2,4,5,5,6]、k = 4
出力: 4
ほどく:
方法①
class Solution {
public:
int findKthLargest(vector<int>& nums, int k) {
//默认大堆 建大堆
priority_queue<int> pq(nums.begin(), nums.end());
//将比第k个大的元素pop出队
for (int i = 0; i < k-1; i++)
{
pq.pop();
}
//取队头元素即为第k个大元素
return pq.top();
}
};
方法②
class Solution {
public:
int findKthLargest(vector<int>& nums, int k) {
//建小堆 建一个有k个元素的小堆
priority_queue<int,vector<int>,greater<int>> pq(nums.begin(),nums.begin()+k);
//堆顶元素即为k个元素中最小元素,将剩余元素入队,如果比队头元素大,则进队,
//将原先队头元素出队,这样可以保证队中元素永远只有k个,将所以元素以此方法进队后,队头元素即为第k个大的元素
for(int i=k;i<nums.size();i++)
{
if(nums[i]>pq.top())
{
pq.pop();
pq.push(nums[i]);
}
}
return pq.top();
}
};