堆(heap):满足某结点,不大于或者小于其父节点的值。
一般来说,常见的堆被默认为 二叉堆
**堆的常见操作 **: STL : priority_queue
(1)取得堆顶的元素)
(2)移除堆顶元素)
(3)插入堆中)
假设以 p<<1 , p<<1|1 完全二叉树下标的性质来建堆, 那么堆顶即为 (heap[1])
在堆中插入元素时,可以先插入到堆尾,然后再向父节点比较进行调整,调节到根结点结束或者符合条件时结束
再移除堆顶对堆进行调整时,实际上就是一个 插入调整的逆向过程 , 将堆尾放置堆顶,然后向下调节
Code : 以下是我写的一个小根堆 . 例题 P3378 【模板】堆
struct Heap{
int heap[maxn];//结点
int size = 0;//大小
/*
push
1.插入堆尾
2.从堆尾开始调整堆
3.根据大/小顶堆的方式调整
*/
void push(int x){
heap[++size] = x;
int p = size,q;
while(p > 1){
q = p >> 1;
if(heap[p] >= heap[q]) return ;
else swap(heap[p] , heap[q]);
p = q;
}
}
int top(){
return heap[1];//堆顶
}
/*
1.将堆尾的数值交给堆顶
2.从堆顶开始向下调整
*/
void pop(){
int p = 1,q;
heap[1] = heap[size--];
while(p*2 <= size){
q = p << 1;
if( q<size && heap[q+1] < heap[q] ) q++; //如果右子点还存在更小的则比较右子点
if(heap[p] <= heap[q]) return;
swap(heap[p],heap[q]);
p = q;
}
}
}hp;
我们明确了堆的操作之后,现在又遇到一个新的问题。如果要将 多个堆进行合并应该怎么操作呢?
把所有的堆重新 放在一个新堆中,那不是(O(nlog^n)) ,这时候要降低合并的复杂度,就需要一种可并数据结构 : 左偏树 (leftist heap/tree)
由于左偏树不是一个完全二叉树,所以要单独记录 左节点与右节点 下标信息
(1.)左偏树具有 堆性质 ,即若其满足小根堆的性质,则对于每个结点 (x) ,有 $v_xle v_{lc},v_xle v_{rc} $
(2.)左偏树具有 左偏性质 ,即对于每个结点 (x) ,有 $dist_{lc}ge dist_{rc} $
(3.) 对于结点 (x) , (dist_x = dist_{rc}+1)
操作: (以下操作默认为小根堆)
(1.)merge 合并
我们假设 (X) 的根节点小于等于 (Y) 的根节点(否则交换(X,Y)),把 (X) 的根节点作为新树 (Z) 的根节点,剩下的事就是合并 (X) 的右子树和 (Y)
合并了 (X) 的右子树和 (Y) 之后,(X) 的右子树的距离可能会变大,当 (X) 的右子树 的距离大于 (X) 的左子树的距离时,性质二会被破坏。在这种情况下,我们只须要交换 (X) 的右子树和左子树。
而且因为 (X) 的右子树的距离可能会变,所以要更新(dist_x = dist_{rc}+1) 这样就合并完了
Code 例题 P3377 【模板】左偏树(可并堆)
#include <cstdio>
#include <iostream>
const int maxn = 1e5+5;
using namespace std ;
#define ls s[x].lc
#define rs s[x].rc
struct leftist_tree{
int dis, val, lc, rc, rt ;
}s[maxn] ;
//路径压缩
int find(int x){
return s[x].rt == x?x:s[x].rt=find(s[x].rt);//对父结点路径压缩
}
int merge(int x,int y){
if(!x || !y) return x+y;
if(s[x].val > s[y].val || (s[x].val == s[y].val && x > y)) swap(x,y);
rs = merge(rs,y);
if(s[ls].dis < s[rs].dis) swap(ls,rs);
s[ls].rt = s[rs].rt = s[x].rt = x;
s[x].dis = s[rs].dis + 1;
return x;
}
void pop(int x){
s[x].val = -1;
s[ls].rt = ls;
s[ls].rt = ls;
s[x].rt = merge(ls,rs);
}
int main(){
int n,m;
cin>>n>>m;
s[0].dis = -1;//根的距离定为-1
for(int i=1;i<=n;i++){
s[i].rt = i, cin>>s[i].val;
}
for(int i=1;i<=m;i++){
int op;
cin>>op;
if(op==1){
int x,y;
cin>>x>>y;
if(s[x].val == -1 || s[y].val == -1) continue;//其中有不存在的部分
int fx = find(x),fy = find(y);
if(fx != fy) s[fx].rt = s[fy].rt = merge(fx,fy);
}else{
int x;
cin>>x;
if(s[x].val == -1) cout<<-1<<endl;
else {
cout<<s[find(x)].val<<endl;
pop(find(x));
}
}
}
return 0;
}