优先队列

Posted by Liao on 2019-08-14

优先队列

一. 定义

优先队列不再遵循先入先出的原则,而是分为两种情况:

  • 最大优先队列,无论入队顺序,当前最大的元素优先出队。
  • 最小优先队列,无论入队顺序,当前最小的元素优先出队。

因此可以用最大堆来实现最大优先队列,每一次入队操作就是堆的插入操作,每一次出队操作就是删除堆顶节点。优先队列的入队和出队的时间复杂度和二叉堆结点上浮下沉的一样,都是O(logn),n为队列元素个数

priority_queue<Type, Container, Functional>

Type 就是数据类型,Container 就是容器类型(Container必须是用数组实现的容器,比如vector,queue等等,但不能用 list。STL里面默认用的是vector),Functional 就是比较的方式。

​ 每一个点都带一个数字,这个数字越小,优先级越高。例如有{A,1} {C,5} {D,3}的元素,由于C的优先级比D低,故D做一个插队操作,排在队列前面:

1
2
priority_queue<int, vector<int>,less<int>> q; // 大根堆
priority_queue<int,vector<int>,greater<int>> q; //小根堆

二. 类型

1、基本类型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<stack>
using namespace std;

int main() {
//基础类型,默认是大根堆
priority_queue<int> a; //等同 priority_queue<int, vector<int>,less<int> > a;
priority_queue<int,vector<int>,greater<int> > c; //小根堆
priority_queue<string> b;
for(int i=0;i<5;i++)
{
a.push(i);
c.push(i);
}
while(!a.empty())
{
cout << a.top() << ' ';
a.pop();
}
cout <<endl;

while(!c.empty())
{
cout <<c.top() << ' ';
c.pop();
}
cout <<endl;

b.push("abc");
b.push("abcd");
b.push("cbd");
while(!b.empty())
{
cout <<b.top()<<' ';
b.pop();
}
cout <<endl;

return 0;
}
/*
输出
4 3 2 1 0
0 1 2 3 4
cbd abcd abc
*/
2、pair<type,type> 类型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<stack>
using namespace std;



int main() {
priority_queue<pair<int,int> > a;
pair<int,int> b(1,2);
pair<int,int> c(1,3);
pair<int,int> d(2,5);
a.push(b);
a.push(c);
a.push(d);
while(!a.empty()){
cout <<a.top().first <<' '<<a.top().second<< endl;
a.pop();
}
return 0;
}

输出结果:
2 5
1 3
1 2

3、自定义类型

HDU1873(看病要排队)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<stack>
using namespace std;

struct hospital
{

int level;
int id;
friend bool operator < (hospital c1,hospital c2)
{
if(c1.level!=c2.level)
return c1.level<c2.level;
else
return c2.id<c1.id;
}
};
int main()
{
int t;
hospital tmp;
string s;
int doc,level;

while(cin >>t)
{
priority_queue<hospital> p[4];
int cnt=0;

while(t--)
{
cin >> s;
if(s=="IN")
{
cnt++;
cin >> doc >> level;
tmp.level = level;
tmp.id = cnt;
p[doc].push(tmp);
}
else if(s=="OUT")
{
cin >>doc;
if(p[doc].empty())
cout << "EMPTY" <<endl;
else
{
tmp = p[doc].top();
p[doc].pop();
cout << tmp.id <<endl;
}

}
}
}
return 0;
}


前k个高频元素

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class Solution {
public:
vector<int> topKFrequent(vector<int>& nums, int k) {
unordered_map<int, int>hash;
for(int n : nums) {
hash[n]++;
}

//排列出现次数,从多到少。 (默认从小到大)
struct myComparison { //自定义小根堆
bool operator()(pair<int, int>&a, pair<int, int>&b) {
return a.second > b.second;
}
};

priority_queue<pair<int, int>, vector<pair<int, int>>, myComparison> que;
for(pair<int, int>a : hash) {
que.push(a);
if(que.size() > k) {
que.pop();
}
}
vector<int>ans;
while(!que.empty()) {
ans.emplace_back(que.top().first);
que.pop();
}
return ans;
}
};

三. 应用

3.1 BFS通过优先队列求最短路

放入队列的点是待处理的点,而出队的点是得到最短路的点

​ 1)每次插入元素的时候,都要把元素和该元素到根节点的距离插入优先队列。

​ 2)由此看出,C的优先级比B高,故会做插队操作:

STL的priority_queue能实现这个插队操作,直接使用即可

parent数组是记录每个结点的父节点。

     3)随后C点出列,表示C已经访问过,则把C的邻接的且没有走过的点塞进优先队列。(可选择B、D、E)

​ 4)同理,(B,3)的优先级最高,故把其放到队首并出列。B的父结点是C。

​ 5)同理,优先级高的(D,4)放到队首出队,与D连接的有B、C、E、F,没有访问过的就只有E、F,故E F入队。

​ 6)由于E点比之前的优先级高,故E放队首,并出队。

​ 7)队列只剩下F没有走,故最后出列。

3.2 执行 K 次操作后的最大分数

传送门

通常在题目中求最大、最小值可以考虑使用堆这种数据结构。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Solution {
public:
long long maxKelements(vector<int>& nums, int k) {
priority_queue<int, vector<int>, less<int>>que; // 建立大根堆

long long ans = 0;
for(int n : nums) {
que.push(n);
}
while(!que.empty() && k--) {
int n = que.top();
ans += n;
que.pop();
que.push((n + 2) / 3); // 最大取整
}
return ans;
}
};