c++中的堆-priority_queue的使用方法
堆作为一个重要的数据结构,应该是接触编程的人员都会了解的,本文是在写leetcode中,由于需要经常使用到堆这一数据结构而写的学习记录帖。即使手撸一个堆并不复杂,但是还是大大拖慢的写题节奏,更何况自己写的堆无论是效率还是安全性咱也没什么信息,这就是STL各个模板发挥的时候了。这里就来简单介绍一下priority_queue,即优先队列。
1 priority_queue的底层实现
PriorityQueue中的元素在逻辑上构成了一棵完全二叉树,但实际这个堆结构在物理存储上是用数组实现的。如果有兴趣了解,可以看看这个博客。
2 priority_queue基本操作
2.1 已有数据类型初始化
小根堆初始化
priority_queue<int,vector<int>,greater<int>> p
大根对初始化
priority_queue<int,vector<int>,less<int>> p
三个参数的意思:
/*
@para p1 :该队列盛放的数据类型
@para p2 :container,需要是数组实现,一般就是vector
@para p3 :比较器,(因为涉及到了比较排序嘛
*/
一般如果我们要使用大根堆且不用自定义的数据结构,只要填写第一个参数即可:
priority_queue<int> p
对于一些已由的数据类型,我们就免去了定义比较器的麻烦,直接使用greater<>
或less<>
即可。值得注意的是,对于tuple
和pair
这样的结构,也已经定义好了比较器:
- pair举例
//默认是使用大根堆
priority_queue<pair<int,int>> pq0;
//小根堆,按照pair的first排,再按照second排序
priority_queue<pair<int,int>,vector<pair<int,int>>,greater<pair<int,int>>> pq1;
//大根堆
priority_queue<pair<int,int>,vector<pair<int,int>>,less<pair<int,int>>> pq2;
- tuple举例
//默认是使用大根堆
priority_queue<tuple<int,int,int>> tp0;
//小根堆,按照tuple的0元素排,再按照1元素排,最后按2元素排
priority_queue<tuple<int,int,int>,vector<tuple<int,int,int>>,greater<tuple<int,int,int>>> tp1;
//大根堆
priority_queue<tuple<int,int,int>,vector<tuple<int,int,int>>,less<tuple<int,int,int>>> tp2;
2.2 自定义数据结构的堆
但是自定义数据结构可以让我们更加自由的编程,所以是必须要探讨的。下面这段程序,我写出了当自定义struct后,我们该如何初始化优先队列。
- 重载
<
符号 - 写一个比较器
(两者实现一个即可)
#include <iostream>
#include <queue>
using namespace std;
struct node{
int sum;
int x;
int y;
node(){};
node(int a,int b,int c){
sum=a;
x=b;
y=c;
}
//一定要加const
friend bool operator < (const node& a, const node&b){
//构建一个大根堆,因为默认是大根堆,而定义的小于符合本意的小于
return a.sum<b.sum;
//如果反着来的话,就是小根堆
}
};
//使用比较器来构建小根堆
class cmp{
public:
bool operator () (const node&a,const node &b){
return a.sum<b.sum;
}
};
int main() {
priority_queue<node> tp;
// priority_queue<node,vector<node>,cmp> tp;//使用比较器版本
for(int i=0;i<10;i++){
tp.push(node(i,1,2));
}
while(!tp.empty()){
cout<<tp.top().sum<<endl;
tp.pop();
}
return 0;
}
2.3 基本操作(和queue相似操作)
- top 访问队头元素
- empty 队列是否为空
- size 返回队列内元素个数
- push 插入元素到队尾 (并排序)
- emplace 原地构造一个元素并插入队列
- pop 弹出队头元素
- swap 交换内容
3 堆的使用场景
这个其实还蛮多的,做题时大概有个感觉:当我们需要不断对数据排序的同时,还需要进行更新,并且只关注局部数据时,堆就是比较好的选择,比如经典的求前K个最大的等等太多了。