平衡树之fhq treap学习笔记(非教学向)

平衡树之fhq treap学习笔记(非教学向)

(非完全版)

开平衡树了,研究了一些平衡树之间的优劣,决定先学fhq treap,splay和替罪羊树?(听说fhq treap和替罪羊比较好写?splay用处较多但较慢?),剩下的平衡树如红黑树,sbtree,avl就等必须要学的时候再学把

现在开始学习fhq treap,后面两个先咕着

1.fhqtreap是什么?

我个人理解是treap的简化版,可以维护一个序列的很多有用的东西,如前驱,后缀,排名等,通过两个主要的操作merge和split平衡的平衡树

2.fhqtreap怎么写?

详见例题

3.fhqtreap怎么用?

我也还不清楚,我才做了一题模板题

推荐题目

t1

【模板】普通平衡树

过于模板,先放代码(没压行友好版)

//I love Nanami Chiaki
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+7;
struct fhqTreap{
    int val,rnd,sz,son[2];
}treap[maxn];
int root=0,tot=1;
inline int newnode(int v){
    int nw=tot++;
    treap[nw].rnd=rand();treap[nw].val=v;treap[nw].sz=1;
    return nw;
}
inline void update(int nw){
    treap[nw].sz=treap[treap[nw].son[0]].sz+treap[treap[nw].son[1]].sz+1;
}
void splitval(int nw,int k,int &x,int &y){
    if (!nw){x=0;y=0;return;}
    if (treap[nw].val<=k){
        x=nw;splitval(treap[nw].son[1],k,treap[nw].son[1],y);
    }
    else{
        y=nw;splitval(treap[nw].son[0],k,x,treap[nw].son[0]);
    }
    update(nw);
}
int merge(int x,int y){
    if (!x || !y) return x^y;
    int re;
    if (treap[x].rnd<treap[y].rnd){
        re=x;treap[x].son[1]=merge(treap[x].son[1],y);
    }
    else{
        re=y;treap[y].son[0]=merge(x,treap[y].son[0]);
    }
    update(re);return re;
}
int kth(int nw,int k){
    if (k==treap[treap[nw].son[0]].sz+1) return nw;
    if (k<=treap[treap[nw].son[0]].sz) return kth(treap[nw].son[0],k);
    return kth(treap[nw].son[1],k-treap[treap[nw].son[0]].sz-1);
}
int main(){
    srand(time(0));
    int n;scanf("%d",&n);
    for (int i=0;i<n;i++){
        int op,v;
        scanf("%d%d",&op,&v);
        if (op==1){
            int x,y;
            splitval(root,v,x,y);
            root=merge(merge(x,newnode(v)),y);
        }
        else if(op==2){
            int x,y,z;
            splitval(root,v,x,z);
            splitval(x,v-1,x,y);
            y=merge(treap[y].son[0],treap[y].son[1]);
            root=merge(merge(x,y),z);
        }
        else if(op==3){
            int x,y;
            splitval(root,v-1,x,y);
            printf("%d\n",treap[x].sz+1);
            root=merge(x,y);
        }
        else if(op==4){
            int nw=kth(root,v);
            printf("%d\n",treap[nw].val);
        }
        else if(op==5){
            int x,y;
            splitval(root,v-1,x,y);
            printf("%d\n",treap[kth(x,treap[x].sz)].val);
            merge(x,y);
        }
        else{
            int x,y;
            splitval(root,v,x,y);
            printf("%d\n",treap[kth(y,1)].val);
            merge(x,y);
        }
    }
    return 0;
} 

是不是一目了然觉得太神奇了?

废话,不是

先讲一下split

split的作用是将一棵树拆成两棵树,这两棵树满足一定的关系?大概就是x内的所有数都小于y内的所有数

有两种分发,一种是按排名分,一种是按val分,都差不多,就是如果这颗树的根节点的值会被我们分到左边的树,就把这个根节点变成我们划分的左边的树的根节点,并且继续分割右子树,把分割出来的左子树,接到当前根节点的右子树上,满足bst的性质,分割出来的右子树就是右子树啦,然后一层一层地返回,最后得到的x和y就是我们需要的两颗树了,对于迭代的理解有点困难的我想了很长时间迭代的问题,但实际上肯定是没问题的,把这里的x和y不要理解成最后我们需要的两棵树的根,而理解成上层循环所需要的两棵树的根就没问题了。

merge的作用就比较简单了,看看程序就懂了?蒟蒻我主要是对split的不理解。但再merge这里有个值得注意的点就是,我才开始学的时候硬是没找到维持平衡的条件在哪里,最后发现只有merge里用到了rnd才恍然大悟。merge的过程就相当于平衡的过程?我先在还没能完全理解这棵树的形态,如果后面的题目我相通了的话会在后面说的

其他就比较好理解了

(未完,待更)

猜你喜欢

转载自www.cnblogs.com/xxjAc/p/12120282.html