[OI学习笔记]配对堆

配对堆是一种可并堆(可以将两个堆合并,且速度快),时间复杂度:

合并merge:O(1)     插入push:O(1)     弹出pop:O(logn)

配对堆不是二叉堆(我一开始不知道这个所以一直搞不懂)

配对堆存树的方式不一样,如图,对于每个节点,有一条边指向最左边的儿子,一条指向他右边的兄弟

和普通树的对比:(灵魂画图)(蓝色为普通方式,红色为做兄弟右儿子)

以下均以大根堆为例

合并:

很简单,比较两树树根,将小的的根作为大的左儿子,然后将小的的根的右兄弟指为大的原来的左儿子

如图:(根的值在点上)

插入:

直接将插入的点作为一个堆,然后和原堆合并

弹出:

把根断开

从左到右依次两两合并,再从左到右依次合并,具体细节可参考代码

代码:

#include<cstdio>

inline void swap(int &x,int &y){
    x^=y^=x^=y;
}

//大根堆 
struct pairing_heap{
    struct node{
        int v,s,r;
    }tree[10010];
    int tot,root;
    pairing_heap(int _tot=0,int _root=0):tot(_tot),root(_root){}
    int Merge(int x,int y){//合并根为x,y的两棵树,返回根 
        if(!x||!y){root=x+y;return x+y;}//有一个是空树(为0)
        if(tree[x].v<tree[y].v)swap(x,y);
        tree[y].r=tree[x].s;tree[x].s=y;
        return root=x;
    }
    int Push(int x){
        tree[++tot].v=x;
        tree[tot].r=tree[tot].s=0;
        return Merge(root,tot);
    }
    void Del(int x){
        tree[root].s=tree[x].r;tree[x].r=0;
    }
    int Pop(){
        int x=tree[root].s,y=tree[x].r;
        if(x==0)return 0;
        Del(x);if(y)Del(y);
        return root=Merge(Merge(x,y),Pop());
    }
    int Top(){
        return tree[root].v;
    }
};

int main(){
    pairing_heap q;
    printf("input “0”for push,“1” for top,“2” for pop or “-1” for exit 
");
    while(1){
        int ques;
        scanf("%d",&ques);
        if(ques==0){
            int x;
            scanf("%d",&x);
            q.Push(x);
        }else if(ques==1){
            printf("%d
",q.Top());
        }else if(ques==2){
            q.Pop();
        }else if(ques==-1){
            break;
        }
    }
    return 0;
} 
本篇文章为SHINE_GEEK原创,转载请注明来源!
written_by:SHINE_GEEK

-------------------------------------
签名:自己选的路,跪着也要走完;理想的实现,需要不懈奋斗!
-------------------------------------
原文地址:https://www.cnblogs.com/sjrb/p/10373313.html