总的来说,可并堆仍然具有堆的性质,即:父节点和孩子节点之间的 key 值大小关系恒定。在此基础上,可并堆增加了可以快速合并的操作。
直观上来讲,因为一个二叉堆是采用完全二叉树的方式进行存储的,这是一个极其平衡的数据结构,但是也正是因为平衡,使得在合并的时候需要花很大力气来再次调整成平衡的结构。
而对于左偏树来说,这是一个左偏的数据结构,因此右边的距离相对较小,此时,每次都让一个堆的右子树和另一个堆进行合并,再调整,这样的时间复杂度就会相对二叉堆的调整低一些。
代码如下:
#include <bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
inline int read(){
int x=0,f=1;char ch;
do{ch=getchar();if(ch=='-')f=-1;}while(!isdigit(ch));
do{x=x*10+ch-'0';ch=getchar();}while(isdigit(ch));
return f*x;
}
struct node{//small-first
#define lson h[x].lc
#define rson h[x].rc
int lc,rc;
int key,dis,f;
}h[maxn];
int n,q;
bool del[maxn];
inline int find(int x){
while(h[x].f)x=h[x].f;
return x;
}
int merge(int x,int y){
if(!x||!y)return x+y;
if(h[x].key>h[y].key||(h[x].key==h[y].key&&x>y))swap(x,y);
rson=merge(rson,y);
h[rson].f=x;
if(h[lson].dis<h[rson].dis)swap(lson,rson);
h[x].dis=h[rson].dis+1;
return x;
}
void erase(int x){
del[x]=1;
h[lson].f=h[rson].f=0;
merge(lson,rson);
}
void read_and_parse(){
n=read(),q=read();
for(int i=1;i<=n;i++)h[i].key=read();
}
void solve(){
while(q--){
int opt=read();
if(opt==1){
int x=read(),y=read();
if(!del[x]&&!del[y]){
int fx=find(x),fy=find(y);
if(fx^fy)merge(fx,fy);//与并查集一样,在合并的时候都是根节点进行合并
}
}else if(opt==2){
int x=read();
if(del[x])puts("-1");
else{
int fx=find(x);
printf("%d
",h[fx].key);
erase(fx);
}
}
}
}
int main(){
read_and_parse();
solve();
return 0;
}