可合并堆-左偏树 初级入门教程

5 篇文章 0 订阅
1 篇文章 0 订阅

引言

对于维护一串数的最大值,最小值,插入,查询,删除优先值。
我们仅需要使用二叉堆也就是STL中的优先队列即可维护!
但如果有多个堆并且需要我们对堆进行合并,该怎么办?
于是引入可合并堆。
可合并堆有四种,这里介绍左偏树。
支持合并,查询,插入,删除优先值操作。

性质

1:根节点大于(小于)任何一个子节点(堆的性质)
2:节点的左儿子的距离不小于右儿子的距离。
3:根节点的距离等于右儿子距离+1
4:一个n个节点的左偏树最大距离为 log(n+1)+1

实现操作

1.合并
我们假设A的根节点小于等于B的根节点(否则交换A,B),把A的根节点作为新树C的根节点,剩下的事就是合并A的右子树和B了。
合并了A的右子树和B之后,A的右子树的距离可能会变大,当A的右子树 的距离大于A的左子树的距离时,性质二会被破坏。在这种情况下,我们只须要交换A的右子树和左子树。

而且因为A的右子树的距离可能会变,所以要更新A的距离=右儿子距离+1。这样就合并完了。
2.插入
相当于一个节点的堆和n个节点的堆合并
3.删除优先值
相当于删除优先值后左右子树合并。

代码实现

洛谷裸题

#include <cstdio>
#define il inline
using namespace std;
const int maxm=110000; 
il int read()
{
    int x=0,w=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-') w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
    return x*w;
}
int son[maxm][2],val[maxm],dis[maxm],fa[maxm],n,m;
il void swap(int &x,int &y)
{
    int tmp=x;
    x=y,y=tmp;
}
int merge(int x,int y)
{
    if (x==0||y==0)
        return x+y;
    if (val[x]>val[y]||(val[x]==val[y]&&x>y))
        swap(x,y);
    son[x][1]=merge(son[x][1],y);
    fa[son[x][1]]=x;
    if (dis[son[x][0]]<dis[son[x][1]])
        swap(son[x][0],son[x][1]);
    dis[x]=dis[son[x][1]]+1;
    return x;
}
il int getf(int x)
{
    while(fa[x]!=x) x=fa[x];
    return x;
}
il void pop(int x)
{
    val[x]=-1;
    fa[son[x][0]]=son[x][0],fa[son[x][1]]=son[x][1];
    merge(son[x][0],son[x][1]);
}
int main()
{
    n=read(),m=read();
    dis[0]=-1;
    for (int i=1;i<=n;i++)
        val[i]=read(),fa[i]=i;
    for(int i=1,x,y,opt;i<=m;i++)
    {
        opt=read();
        if(opt==1)
        {
            x=read(),y=read();
            if(val[x]==-1||val[y]==-1||x==y)
             continue;
            int fx=getf(x),fy=getf(y);
            if(fx!=fy)
             merge(fx,fy);
        }
        else
        {
            x=read();
            if(val[x]==-1)
             printf("-1\n");
            else
            {
                int fa=getf(x);
                printf("%d\n",val[fa]);
                pop(fa);
            }
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值