引入
说白了,就是一种功能强大的队列。如果不太清楚队列,可以看看我这篇博客。
它的功能强大在哪里呢?
四个字:自动排序。
优先队列的头文件声明
首先,你需要
#include<queue>
using namespace std;
这两个头文件。
其次,一个优先队列声明的基本格式是:
priority_queue<结构类型> 队列名;
比如:
priority_queue <int> i;
priority_queue <double> d;
不过,我们最为常用的是这几种:
priority_queue <*node*> q;
//node是一个结构体
//结构体里重载了‘<’小于符号
priority_queue <int,vector<int>,greater<int> > q;
//不需要#include<vector>头文件
//注意后面两个“>”不要写在一起,“>>”是右移运算符
priority_queue <int,vector<int>,less<int> >q;
我们将在下文来讲讲这几种声明方式的不同。
优先队列的基本操作,与队列的基本操作如出一辙。
以一个名为q的优先队列为例。
q.size();//返回q里元素个数
q.empty();//返回q是否为空,空则返回1,否则返回0
q.push(k);//在q的末尾插入k
q.pop();//删掉q的第一个元素
q.top();//返回q的第一个元素
q.back();//返回q的末尾元素
优先队列的特性:上文已经说过了,自动排序。
怎么个排法呢? 在这里介绍一下:
默认的优先队列(非结构体结构)
priority_queue q;
这样的优先队列是怎样的?让我们写程序验证一下。
#include<cstdio>
#include<queue>
using namespace std;
priority_queue <int> q;
int main()
{
q.push(10),q.push(8),q.push(12),q.push(14),q.push(6);
while(!q.empty())
printf("%d ",q.top()),q.pop();
程序大意就是在这个优先队列里依次插入10、8、12、14、6,再输出。
结果是什么呢?
14 12 10 8 6
也就是说,它是按从大到小排序的!
默认的优先队列(结构体,重载小于)
先看看这个结构体是什么。
struct node
{
int x,y;
bool operator < (const node & a) const
{
return x<a.x;
}
};
这个node结构体有两个成员,x和y,它的小于规则是x小者小。
再来看看验证程序:
#include<cstdio>
#include<queue>
using namespace std;
struct node
{
int x,y;
bool operator < (const node & a) const
{
return x<a.x;
}
}k;
priority_queue <node> q;
int main()
{
k.x=10,k.y=100; q.push(k);
k.x=12,k.y=60; q.push(k);
k.x=14,k.y=40; q.push(k);
k.x=6,k.y=80; q.push(k);
k.x=8,k.y=20; q.push(k);
while(!q.empty())
{
node m=q.top(); q.pop();
printf("(%d,%d) ",m.x,m.y);
}
}
程序大意就是插入(10,100),(12,60),(14,40),(6,20),(8,20)这五个node。
再来看看它的输出:
(14,40) (12,60) (10,100) (8,20) (6,80)
它也是按照重载后的小于规则,从大到小排序的。
less和greater优先队列
还是以int为例,先来声明:
priority_queue <int,vector,less > p;
priority_queue <int,vector,greater > q;
话不多说,上程序和结果:
#include<cstdio>
#include<queue>
using namespace std;
priority_queue <int,vector<int>,less<int> > p;
priority_queue <int,vector<int>,greater<int> > q;
int a[5]={10,12,14,6,8};
int main()
{
for(int i=0;i<5;i++)
p.push(a[i]),q.push(a[i]);
printf("less<int>:")
while(!p.empty())
printf("%d ",p.top()),p.pop();
pritntf("\ngreater<int>:")
while(!q.empty())
printf("%d ",q.top()),q.pop();
}
结果:
less:14 12 10 8 6
greater:6 8 10 12 14
所以,我们可以知道,less是从大到小,greater是从小到大。
作个总结,为了装13方便,在平时,建议大家写:
priority_queue<int,vector<int>,less<int> >q;
priority_queue<int,vector<int>,greater<int> >q;
平时如果用从大到小不用后面的vector,less,可能到时候要改成从小到大,你反而会搞忘怎么写greater,反而得不偿失。
底层实现
优先级队列是在堆的基础上进行的一个扩展,主要是运用大堆来实现优先级队列,让优先度高的出队列,最大优先队列可以选用大堆,最小优先队列可以选用小堆来实现。
下面以最大优先队列来讲解其原理。
最大优先队列一般包括将一个元素插入到集合S中、返回集合S中具有最大key的元素、返回并删除集合S中具有最大key的元素等。
//堆的实现代码如下 :
#include <vector>
#include <assert.h>
using namespace std;
template<class T>
struct Less
{
bool operator()(const T& left, const T& right)
{
return left < right;
}
};
template<class T>
struct Greater
{
bool operator()(const T& left, const T& right)
{
return left > right;
}
};
template<class T, class Compare = Less<T>>
class Heap
{
public:
Heap()
{}
Heap(const T array[], size_t size)
{
_heap.reserve(size);
for (size_t i = 0; i < size; i++)
{
_heap.push_back(array[i]);
}
for (int j = (_heap.size() - 2) / 2; j >= 0; j--)
{
_AdjustDown(j);
}
}
void Insert(const T& data)
{
assert(data);
assert(!_heap.empty());
_heap.push_back(data);
_Adjustup(_heap.size() - 1);
}
void Remove()
{
assert(!_heap.empty());
swap(_heap[0], _heap[size() - 1]);
_heap.pop_back();
_AdjustDown(0);
}
void Push(const T& x)
{
//插入到尾部,再从最后一个元素开始向上调整
_a.push_back(x);
AdjustUp(_a.size()-1);
}
void Pop()
{
//将堆顶与最后一个元素交换,再从堆顶下滑调整
assert(!_a.empty());
swap(_a[0], _a[_a.size() - 1]);
_a.pop_back();
if (_a.size()>1) //如果堆中的元素大于一个,再进行调整
{
AdjustDown(0, _a.size());
}
}
size_t Size()const
{
return _heap.size();
}
bool Empty()const
{
return _heap.empty();
}
const T& Top()const
{
assert(!_heap());
return _heap[0];
}
void _AdjustDown(size_t parent)
{
assert(parent);
assert(!_heap.empty());
size_t child = parent * 2 - 1;
while (parent < _heap.size())
{
Compare com;
if (child + 1<_heap.size()&& com(_heap[child + 1], _heap[child]))
{
++child;
}
if (child<_heap.size() && com(_heap[child], _heap[parent]))
{
swap(_heap[child], _heap[parent]);
parent = child;
child = parent * 2 + 1;
}
else
break;
}
}
void _AdjustUp(size_t child)
{
assert(!_heap.empty());
while(child)
{
Compare com;
size_t parent = (child - 1) / 2;
if (com(_heap[child], _heap[parent]))
{
swap(_heap[child], _heap[parent]);
child = parent;
}
else
break;
}
}
private:
vector<T> _heap;
};
template<class T, class Compare = Less<T>>
class PriorityQueue
{
public:
PriorityQueue()
{}
PriorityQueue(T* a, size_t size)
:_h(a, size)
{
}
void Push(const T& data)
{
_hp.push(data);
}
void Pop()
{
_hp.pop();
}
const T& Top()const
{
_hp.Top();
}
size_t Size()const
{
return _heap.size();
}
bool Empty()const
{
return _hp.Empty();
}
protected:
Heap<T, Compare> _hp;
};
文章参考:https://blog.youkuaiyun.com/qq_19656301/article/details/82490601
https://blog.youkuaiyun.com/archyli/article/details/71860651