优先级队列 priority_queue
1. priority_queue 的介绍
priority_queue 相当于数据结构中的大堆,当插入数据的时候会自动建大堆,因此最大的数据将会被排在堆顶:
- 优先队列是一种容器适配器,根据严格的弱排序标准,它的第一个元素总是它所包含的元素中最大的。
- 此上下文类似于堆,在堆中可以随时插入元素,并且只能检索最大堆元素(优先队列中位于顶部的元
素)。 - 优先队列被实现为容器适配器,容器适配器即将特定容器类封装作为其底层容器类,queue提供一组特定的成员函数来访问其元素。元素从特定容器的“尾部”弹出,其称为优先队列的顶部。
2. priority_queue 的使用
建大堆
默认建堆就是大堆:
priority_queue<int> bigq; //默认建大堆
建小堆
一般使用标准容器类 vector 或 deque 来建堆,一般情况下默认使用vector
priority_queue<int ,vector<int> , greater<int>> q; //小堆
push(val)
插入一个值(默认建大堆)
priority_queue<int> q; // 大堆
q.push(1);
q.push(2);
q.push(3);
q.push(4);
q.push(5);
因为每插入一个值都会进行堆排序,所以目前 q 内数据的顺序是 :
5 4 3 2 1
empty()
返回是否为空,老熟人了,不多介绍
size()
返回优先级队列内有效数据的个数
priority_queue<int> q;
q.push(5);
q.push(4);
q.push(3);
q.push(2);
q.push(1);
cout << q.size() << endl; //输出结果为 5
top()
返回顶部数据,因为默认是大堆,因此返回值一定是此队列中最大的数
priority_queue<int> q; //默认建大堆
q.push(5);
q.push(4);
q.push(3);
q.push(2);
q.push(1);
cout << q.top() << endl; //输出结果为 5
priority_queue 使用迭代器构造
使用vector和他的迭代器构造一个priority_queue
vector<int> v;
v.push_back(5);
v.push_back(4);
v.push_back(3);
v.push_back(2);
v.push_back(1);
//默认大堆
priority_queue<int> pq(v.begin(), v.end());;
3. priority_queue 的自主实现
底层容器的复用
自主实现过程中,重点在于要复用的容器,即vector和deque,
因为这两个容器支持以下操作:
empty():检测容器是否为空
size():返回容器中有效元素个数
front():返回容器中第一个元素的引用
push_back():在容器尾部插入元素
pop_back():删除容器尾部元素
最重要的是能通过[]来对数据进行遍历和排列
仿函数的应用
因为在建堆的过程中,分为大堆和小堆
我们的代码不可能说把建小堆和建大堆的代码都写一份,这样子就很麻烦,也没法进行很好的调用
因为建小堆和建大堆的过程最终不同的地方就在于父节点和子节点的位置交换,即 > < 号的使用上
那就可以通过模板参数和仿函数来实现
比如,用< 号的比较来建大堆
template<class T>
class Less //小于号,建大堆
{
public:
bool operator()(const T& x, const T& y)
{
return x < y;
}
};
用 > 号的比较来建小堆
template<class T>
class Greater //大于号,建小堆
{
public:
bool operator()(const T& x, const T& y)
{
return x > y;
}
};
最终, > 和 < 的比较被封装在一个类中,而这个类只有一个作用,就是重载一个 () 运算符,原本正常类的调用:
Greater g;
g.operator()(x,y);
但这样很难看,可以直接g(x,y),这样看起来非常像函数调用,因此被称为仿函数
代码实现
#pragma once
#include<iostream>
#include<vector>
using namespace std;
//仿函数控制大小
template<class T>
class Less //小于号,建大堆
{
public:
bool operator()(const T& x, const T& y)
{
return x < y;
}
};
template<class T>
class Greater //大于号,建小堆
{
public:
bool operator()(const T& x, const T& y)
{
return x > y;
}
};
namespace cris
{
template<class T , class Container = vector<T> , class Compare = Less<T>> //默认建大堆
class priority_queue
{
public:
void adjust_down(int parent)
{
Compare com;
int child = parent * 2 + 1;
int size = _con.size();
while (child < size)
{
//if (child + 1 < size && _con[child] < _con[child + 1])
if (child + 1 < size && com(_con[child] , _con[child + 1]))
{
child++;
}
//if (_con[parent] < _con[child])
if (com(_con[parent] , _con[child]))
{
swap(_con[parent], _con[child]);
parent = child;
child = parent * 2 - 1;
}
else
{
break;
}
}
}
void adjust_up(int child)
{
Compare com;
int parent = (child - 1) / 2;
while (child > 0)
{
//if (_con[parent] < _con[child])
if (com(_con[parent] , _con[child]))
{
swap(_con[child], _con[parent]);
child = parent;
parent = (child - 1) / 2;
}
else
{
break;
}
}
}
void push(const T& val)
{
_con.push_back(val);
adjust_up(_con.size() - 1);
}
void pop()
{
swap(_con[0], _con[_con.size() - 1]);
_con.pop_back();
adjust_down(0);
}
bool empty()
{
return _con.empty();
}
const T& top()
{
return _con.front();
}
size_t size()
{
return _con.size();
}
private:
Container _con;
};
}