模板 堆
堆是一颗具有特定性质的二叉树,堆的基本要求就是堆中所有结点的值必须大于或等于(或小于或等于)其孩子结点的值,这也称为堆的性质;堆还有另一个性质,就是当 h > 0 时,所有叶子结点都处于第 h 或 h - 1 层(其中 h 为树的高度,完全二叉树),也就是说,堆应该是一颗完全二叉树;
一般堆使用数组存储,求父结点操作为向右移一位(即除以2),求左儿子为左移一位,右儿子为左移一位加1
这里操作以最小堆为例子
添加操作
推荐操作原理就是在堆末尾添加数据,然后不断和父节点比较。如果父节点比它大不满足最小堆性质,两者就交换数据直到父节点比其小
删除根操作
代码的操作就是先将根和堆末尾数据交换,然后不断的与较小的子数交换直到满足性质
例题洛谷P3378
#include <iostream>
#include<algorithm>
#include<cstdio>
#include<vector>
#include<queue>
#include<stack>
#include<cstring>
using namespace std;
long long n,x,heap[1000005],t;
int op,cnt,now,next;
inline void swap(long long &x,long long &y){
t=y;
y=x;
x=t;
}
void push(long long x){
heap[++cnt]=x;
now=cnt;
while(now){
next=(now>>1);//寻找父亲
if(heap[next]>heap[now]){//如果父亲比儿子大 需要交换
swap(heap[next],heap[now]);
now=next;
}
else break;//父亲已经比儿子小了 符合要求
}
}
void pop(){
swap(heap[cnt],heap[1]);
cnt--;
now=1;
while((now<<1)<=cnt){
next=(now<<1);
if(next+1<=cnt&&heap[next+1]<heap[next]) next++;//如果右儿子比左儿子更小,则选择右儿子交换
if(heap[now]>heap[next]){
swap(heap[next],heap[now]);
now=next;
}
else break;//已符合要求
}
}
int main(){
//freopen("testdata.in", "r", stdin);
cin>>n;
while(n--){
cin>>op;
if(op==1){
scanf("%lld",&x);
push(x);
}
else if(op==2){
cout<<heap[1]<<endl;
}
else {
pop();
}
}
return 0;
}
补充:利用STL的优先队列可以轻松实现堆
#include<queue>
priority_queue<int> q;//这是一个大根堆q
priority_queue<int,vector<int>,greater<int> >q;//这是一个小根堆q
//优先队列的操作
q.top()//取得堆顶元素,并不会弹出
q.pop()//弹出堆顶元素
q.push()//往堆里面插入一个元素
q.empty()//查询堆是否为空,为空则返回1否则返回0
q.size()//查询堆内元素数量
同时如果出现删除任意一个点的要求时,开一个数组del,在要删除其他元素的时候直接就标记一下del[i]=1,这里的下标是元素的值,然后在查询的时候碰到这个元素被标记了直接弹出然后继续查询就可以了
利用队列AC代码
#include <iostream>
#include<algorithm>
#include<cstdio>
#include<vector>
#include<queue>
#include<stack>
#include<cstring>
using namespace std;
priority_queue<int,vector<int>,greater<int> >q;
int n,op,x;
int main(){
cin>>n;
while(n--){
cin>>op;
if(op==1){
cin>>x;
q.push(x);
}
else if(op==3){
q.pop();
}
else cout<<q.top()<<endl;
}
return 0;
}
// freopen("testdata.in", "r", stdin);

浙公网安备 33010602011771号