您的位置:首页 > 其它

左偏树简介(洛谷P3377题解)

2017-09-25 21:25 295 查看
(感觉自己算法学得好乱啊。。。)

算法简介

左偏树(Leftist Tree)是一种可并堆的实现。所谓可并堆,即既满足堆的性质,又支持堆的合并。

左偏树是一棵二叉树,它的节点除了和二叉树的节点一样具有左右子树指针外,还有两个属性,键值和距离。

算法实现

性质

每个节点都有一个“距离”,即该节点左右儿子距离的最小值+1(如果有一个儿子是没有的距离就是0)。而左偏树的每一个节点都满足这样一个性质:其左儿子的距离大于等于其右儿子的距离。

核心

左偏树算法的核心就是合并操作。对于两棵树x和y,有如下操作进行合并(实际合并时只需拿出根节点即可):

①如果其中一棵是空树,返回另一棵。

②如果x根节点的优先级比y的大,则交换x与y。

③递归合并y与x的右子树。

④如果右儿子的距离大于左儿子了,就把两个儿子交换(保证左偏性)

⑤更新x的距离。

模板

洛谷P3377为例:

非路压版:

#include<cstdio>
#include<cstring>
#include<algorithm>
#define MAXN 100000
using namespace std;
struct node{
int ls,rs;//左儿子与右儿子
int v,dis;//该节点的值与距离
};
int n,m,fa[MAXN+5];
bool f[MAXN+5];
node a[MAXN+5];
//fread读优
inline char readc(){
static char buf[100000],*l=buf,*r=buf;
if (l==r) r=(l=buf)+fread(buf,1,100000,stdin);
if (l==r) return EOF;
return *l++;
}
inline int _read(){
int num=0; char ch=readc();
while (ch<'0'||ch>'9') ch=readc();
while (ch>='0'&&ch<='9') { num=num*10+ch-48; ch=readc(); }
return num;
}
inline int findfather(int x){//这里不能路压,因为不是并查集
while (fa[x]) x=fa[x];
return x;
}
int merge(int x,int y){
if (x*y==0) return x+y;//如果其中一个为空返回另一个
if (a[x].v>a[y].v||(a[x].v==a[y].v&&x>y)) swap(x,y);//如果x的优先级比y的大,交换(这里是小根堆)
a[x].rs=merge(a[x].rs,y);//递归处理
fa[a[x].rs]=x;//更新右子树
if (a[a[x].ls].dis<a[a[x].rs].dis) swap(a[x].ls,a[x].rs);
//如果右儿子的距离大于左儿子,就把两个儿子交换
a[x].dis=a[a[x].rs].dis+1;//更新距离
return x;
}
int main(){
n=_read(); m=_read();
memset(f,false,sizeof(f));
for (int i=1;i<=n;i++){
a[i].v=_read();
a[i].dis=0;
}
for (int i=1;i<=m;i++){
int flag=_read();
if (flag==1){
int x=_read(),y=_read();
if (f[x]||f[y]) continue;
int fx=findfather(x),fy=findfather(y);
if (fx!=fy)
merge(fx,fy);
}
else{
int x=_read();
if (f[x]){
printf("-1\n");
continue;
}
int fx=findfather(x);
printf("%d\n",a[fx].v);
f[fx]=true;
fa[a[fx].ls]=fa[a[fx].rs]=0;
merge(a[fx].ls,a[fx].rs);
}
}
return 0;
}


路压版(其实没什么变化):

#include<cstdio>
#include<cstring>
#include<algorithm>
#define MAXN 100000
using namespace std;
struct node{
int ls,rs;
int v,dis;
};
int n,m,fa[MAXN+5];
bool f[MAXN+5];
node a[MAXN+5];
inline char readc(){
static char buf[100000],*l=buf,*r=buf;
if (l==r) r=(l=buf)+fread(buf,1,100000,stdin);
if (l==r) return EOF;
return *l++;
}
inline int _read(){
int num=0; char ch=readc();
while (ch<'0'||ch>'9') ch=readc();
while (ch>='0'&&ch<='9') { num=num*10+ch-48; ch=readc(); }
return num;
}
int findfather(int x){//路压
if (fa[x]==x) return x;
return fa[x]=findfather(fa[x]);
}
int merge(int x,int y){//合并操作一个样
if (x*y==0) return x+y;
if (a[x].v>a[y].v||(a[x].v==a[y].v&&x>y)) swap(x,y);
a[x].rs=merge(a[x].rs,y);
fa[a[x].rs]=x;
if (a[a[x].ls].dis<a[a[x].rs].dis) swap(a[x].ls,a[x].rs);
if (a[x].rs) a[x].dis=a[a[x].rs].dis+1;
return x;
}
int main(){
n=_read(); m=_read();
memset(f,false,sizeof(f));
for (int i=1;i<=n;i++){
a[i].v=_read();
fa[i]=i;
}
for (int i=1;i<=m;i++){
int flag=_read();
if (flag==1){
int x=_read(),y=_read();
if (f[x]||f[y]) continue;
int fx=findfather(x),fy=findfather(y);
if (fx!=fy){
merge(fx,fy);
}
}
else{
int x=_read();
if (f[x]){
printf("-1\n");
continue;
}
int fx=findfather(x);
printf("%d\n",a[fx].v);
f[fx]=true;
fa[a[fx].ls]=a[fx].ls; fa[a[fx].rs]=a[fx].rs;
fa[fx]=merge(a[fx].ls,a[fx].rs);
a[fx].ls=a[fx].rs=0;
}
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: