堆
(二叉)堆是一个数组,可以被看成近似的完全二叉树
父节点/左孩子/右孩子
int parent(int i){
return i/2;
}
int left(int i){
return 2*i;
}
int right(int i){
return 2*i+1;
} 二叉堆可以分为最大堆和最小堆
最大堆:某个节点至多与其父节点一样大
最小堆:除了根以外的所有节点i,都有A[parent(i)]<=A[i]
堆排序算法我们可以使用最大堆,最小堆通常用于构造优先队列
堆的高度定义为根节点的高度(从下往上数0,1,2...)
堆结构的一些基本操作的运行时间至多与树的高度成正比,即O(logn)
维护最大堆--O(logn)
void max_heapify(int A[],int i,int length){//数组传参不用带引用
int l = left(i);
int r = right(i);
int largest;
if(l<=length && A[l]>A[i]){
largest = l;
}else{
largest = i;
}
if(r<=length && A[r]>A[largest]){
largest = r;
}
if(largest != i){
swap(A[i],A[largest]);
max_heapify(A,largest,length);
}
} 构造堆--O(n)
void build_max_heap(int A[],int length){//这里下标是以1开始的
for(int i=length/2;i>=1;i--){
max_heapify(A,i,length);
}
} 堆排序--O(nlogn)
void heapsort(int A[],int length){//从小到大的堆排序
build_max_heap(A,length);
for(int i=length;i>=2;i--){
swap(A[1],A[i]);
length = length-1;
max_heapify(A,1,length);
}
} 最小堆
void min_heapify(vector<pair<int,int> > &A,int i,int length){//数组传参不用带引用
int l = left(i)+1;
int r = right(i)+1;
int shortest;
if(l<=length && A[l].first<A[i].first){
shortest = l;
}else{
shortest = i;
}
if(r<=length && A[r].first<A[shortest].first){
shortest = r;
}
if(shortest != i){
swap(A[i],A[shortest]);
min_heapify(A,shortest,length);
}
}
//这里下标是以0开始的
void build_min_heap(vector<pair<int,int> > &A,int length){
for(int i=length/2-1;i>=0;i--){
min_heapify(A,i,length);
}
} 优先队列的一些操作--O(logn)
1. Insert - (push)
堆的插入就是把新的元素放到堆底,然后检查它是否符合堆的性质,如果符合就丢在那里了,如果不符合,那就和它的父亲交换一下,一直交换交换交换,直到符合堆的性质,那么就插入完成了
void swap(int &x,int &y){int t=x;x=y;y=t;}//交换函数
int heap[N];//定义一个数组来存堆
int siz;//堆的大小
void push(int x){//要插入的数
heap[++siz]=x;
now=siz;
//插入到堆底
while(now){//还没到根节点,还能交换
ll nxt=now>>1;//找到它的父亲
if(heap[nxt]>heap[now])swap(heap[nxt],heap[now]);//父亲比它大,那就交换
else break;//如果比它父亲小,那就代表着插入完成了
now=nxt;//交换
}
return;
}2.Minimun
3.Extract-min
4.Decrease-key
5.Delete - (pop)
删除操作,但是在实际的代码操作中,并不是这样进行删除操作的,有一定的微调,代码中是直接把堆顶和堆底交换一下,然后把交换后的堆顶不断与它的子节点交换,直到这个堆重新符合堆性质
void pop(){
swap(heap[siz],heap[1]);siz--;//交换堆顶和堆底,然后直接弹掉堆底
int now=1;
while((now<<1)<=siz){//对该节点进行向下交换的操作
int nxt=now<<1;//找出当前节点的左儿子
if(nxt+1<=siz&&heap[nxt+1]<heap[nxt])nxt++;//看看是要左儿子还是右儿子跟它换
if(heap[nxt]<heap[now])swap(heap[now],heap[nxt]);//如果不符合堆性质就换
else break;//否则就完成了
now=nxt;//往下一层继续向下交换
}
}优先队列(堆)的STL实现
如果开了O2优化能使程序的编译效率大大提升
注意堆里面的大小,并非传统意义下的大小
定义一个优先队列:
首先你需要一个头文件:#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()//查询堆内元素数量
额外提一下重载在堆的使用
bool operator < (node a , node b) {
return a.w > b.w;
}熟悉优先队列STL的人都知道,priority_queue默认大根堆,但如果我们想把他直接用去优化dijkstra肯定是不行的,所以我们可以以上的一段代码加入来将priority_queue变为小根堆,定义优先队列这样写priority_queue <node> q就行了</node>
当然,你也可以将重载运算符写在sruct里:
struct node {
int id , w;
bool operator < (const node &b) const {
return w > b.w;
}
}如果你不使用重载运算符的话,就得这么写:
struct node {
int id , w;
}
struct cmp {
bool operator ()(node a , node b) {
return a.w > b.w;
}
priority_queue <node , vector <node> , cmp> q;参考文献:
https://www.cnblogs.com/henry-1202/p/9307927.html
https://www.luogu.com.cn/blog/Jydon/wzx-AK-IOI


京公网安备 11010502036488号