priority_queue
一、priority_queue的使用
优先队列是一种容器适配器,根据严格的弱排序标准,它的第一个元素总是它所包含的元素中最大的。priority_queue实际上是堆,在堆中可以随时插入元素,并且只能检索最大堆元素(优先队列中位于顶部的元素)(默认情况下,priority_queue是大根堆)。
函数 | 说明 |
---|---|
push(x) | 在优先级队列中插入元素x |
pop() | 删除优先级队列中最大(最小)元素,即堆顶元素 |
top() | 返回优先级队列中最大(最小元素),即堆顶元素 |
empty() | 检测优先级队列是否为空,是返回true,否则返回false |
这里作者想强调的并不是这些函数,因为这些函数是适配器都有的函数(stack、queue),而是强调priority_queue的模板参数。
- 默认情况下,priority_queue是大堆,默认使用vector作为其底层存储数据的容器。
根节点为最大元素
vector<int> v{3,2,7,6,0,4,1,9,8,5};
priority_queue<int> q1;
for (auto& e : v)
q1.push(e);
cout << q1.top() << endl; // 9
- 如果要创建小堆,将第三个模板参数换成greater比较方式,第二个模板参数是底层容器,默认是vector,也可以是deque。
注:
- 这里底层容器不能是list,因为向上向下调整要支持随机访问,而list不支持随机访问,故不能使用list
- 如果是自定义类,需要重载“>”或者“<”
- 这里说明一下模板参数的第三个参数,这是STL的另一大组件——仿函数,仿函数实际是类,对这个类进行了“()”运算符重载。(文章后面模拟实现priority_queue中对继续说明仿函数)
#include <vector>
#include <queue>
#include <functional> // greater算法的头文件
int main() {
vector<int> v{3,2,7,6,0,4,1,9,8,5};
priority_queue<int, vector<int>, greater<int>> q2(v.begin(), v.end());
cout << q2.top() << endl;
}
3.也可以自己提供比较器规则。(这里就不举例了,可以看后面模拟实现的比较定义)
二、priority_queue的模拟实现
- 仿函数类:重载"()" 运算符的一个类
使用仿函数,自定义优先级,不需要修改代码,增加代码的灵活性。
template <class T>
struct Greater {
bool operator()(const T& a, const T& b) {
return a > b;
}
};
template <class T>
struct Less {
bool operator()(const T& a, const T& b) {
return a < b;
}
};
2.priority_queue的具体模拟实现
(这里的代码并没有全部模拟,这个代码也并不是注重堆的向上/向下调整算法(虽然代码已具体实现),这份代码注重的就是模板的第二个和第三个参数,主要是适配器是由底层容器作为模板参数实现,还有仿函数的思想)
template<class T, class Container, class Compare>
class Priority_Queue {
public:
void push(const T& val) {
_con.push_back(val);
//向上调整
shiftUp(_con.size() - 1);
}
void pop() {
swap(_con[0], _con[_con.size() - 1]);
_con.pop_back();
//向下调整
shiftDown(0);
}
T& top() {
return _con.front();
}
size_t size() {
return _con.size();
}
bool empty() {
return _con.empty();
}
private:
Container _con;
Compare _com;
void shiftUp(size_t child) {
size_t parent = (child - 1) >> 1; //父亲结点为 (该结点-1)/2
while (child > 0) {
//if (_con[child] > _con[parent]) {
if (_com(_con[child], _con[parent])) {
swap(_con[child], _con[parent]);
child = parent;
parent = (child - 1) >> 1;
} else {
break;
}
}
}
void shiftDown(size_t parent) {
size_t child = 2 * parent + 1;
while (child < _con.size()) {
//if (child + 1 < _con.size() && _con[child + 1] > _con[child]) {
if (child + 1 < _con.size() && _com(_con[child + 1], _con[child])) {
++child;
}
//if (_con[child] > _con[parent]) {
if (_com(_con[child], _con[parent])) {
swap(_con[child], _con[parent]);
parent = child;
child = 2 * parent + 1;
}
else {
break;
}
}
}
};