左偏树——可并堆

左偏树

简介:

  1. 左偏树中的一个节点,如果它的左子树右子树 NULL N U L L ,则称它是一个外结点
  2. 在左偏树中,距离( dist d i s t )为该点到最近外结点的距离。
  3. 堆性质——在非叶节点中满足 val(x)val(lson),val(x)val(rson) v a l ( x ) ≥ v a l ( l s o n ) , v a l ( x ) ≥ v a l ( r s o n ) (大根堆)
  4. 左偏性质—— dist(lson)dist(rson) d i s t ( l s o n ) ≥ d i s t ( r s o n ) ,这样就能满足合并的复杂度 Θ(log(s1+s2)) Θ ( l o g ( s 1 + s 2 ) )
  5. 另外,可以想到 dist(x)=dist(rson)+1 d i s t ( x ) = d i s t ( r s o n ) + 1
  6. 不难推出,左偏树保证了每个非空儿子节点的右子树一定也是左偏树。
  7. 最后,一棵左偏树的高度为 log(n+1)1 l o g ( n + 1 ) − 1 ,在最右链的长度至多有 log(n+1) l o g ( n + 1 ) 个节点。

常规操作:

  • pop:弹掉该点,即合并它的左子树和右子树。
  • top:返回堆顶的val。
  • merge:对于需要合并的 xy x , y ;先将x的右子树与y合并,再合并x。其中还要if一些满足左偏树的性质及 dist d i s t 计算。

Code(模板):

struct node{
    int l,r,val,dist;
}tree[N*20];

struct Left_Tree{

    int init(){
        tat=1;
        tree[0]=(node){0,0,0,INF};
    }

    int newnode(int p){
        tree[tat]=tree[p];
        return tat++;
    }

    void merge(int x,int y){
        if(!x || !y)return x+y;
        if(tree[x].val>tree[y].val)swap(x,y);//取决于大、小顶堆 
        int o=newnode(x);
        tree[o].r=merge(tree[o].r,y);//先合并右子树,因为右子树保证是左偏树 
        if(tree[tree[o].l].dist//满足左儿子的dist>右儿子的 
        tree[o].dist=tree[tree[o].r].dist+1;
        return o;
    }

    int top(int x){
        return tree[x].val;
    }

    void push(int &x,int y,int val){
        int o=newnode(0);
        tree[o].val=val;
        x=merge(x,o);//合并新的点
    }

    void pop(int &x){
        int tmp=merge(tree[x].l,tree[x].r);//合并儿子
        tree[x]=(node){0,0,0,INF};
    }

}Ltree;

Summary:

  • 左偏树是一个非常稳定的数据结构,尤其是它的合并,复杂度是很优秀的。
  • 再者,一般是解决一些多维的问题,可以通过堆的排序降一维,达到一个 log l o g

你可能感兴趣的:(数据结构)