zoukankan      html  css  js  c++  java
  • 平衡树之非旋Treap

    平衡树(二叉树)

    线段树不支持插入or删除一个数于是平衡树产生了
    常见平衡树:treap(比sbt慢,好写吧),SBT(快,比较好写,有些功能不支持),splay(特别慢,复杂度当做根号n来用,功能强大,不好写),rbt(红黑树,特别快),//替罪羊树,朝鲜树
    晚上要讲的不旋转平衡树:

    平衡树:

    节点的左儿子中的每一个一定比他小,右儿子中的每一个一定比他大
    那么它的中序遍历是有序的
    用下标建树,那么区间询问的话就是求一棵子数和子树根和领一棵子数的一部分

    treap:

    tree+heap,平衡树和heap的性质是矛盾的,所以每个节点存一个key和value
    key值满足heap性质,value满足平衡树的性质,这样的树叫做treap?

    插入:

    插入的新节点的key值随机,调用rand函数(这样保证树的深度一定是logn的)改变树的形态使它重新满足hea与平衡树性质

    操作1.merge:

    merge(P1,P2):把以p1为根的treap和以p2为根的treap合并成一个treap(p1中的所有制小于

    操作2.split:

    把以p为根的treap中拿出k小的数,组成一个新treap
    保证原先树中的所有数>新树中所有数

    可持久化treap :

    插:

    建一个只有一个点的树(要插得数)例如(2.33)把(1,2)splay出来,再把新树(2.33)和(1,2)merge起来,再把(1,2,2.33)和(4,5)merge 一下

    删除一个:

    如删除(2.33),先把split(treap,3),此时把splay把(1,2)与(2.33,4,5)分离在split(treep2,1),此时(2.33)与(4,5)分离
    在merge(treap1,treap3)合并即把(1,2),(4,5)合并,那么2.33就没了

    实际操作

    merge时,找key值最大的作为新treap的根,不是p1就是p2
    1要是p1.p>=p2.p此时p1作为新根,那么p1的左儿子不会变换,右子树就是p1的右子树和p2 merge 一下,即 merge(p1.r,p2);
    2要是p2.p>p1.p此时p2作为新根,那么p2的右儿子不会变换,左儿子就是p2的
    左子树 和 p1 mege 一下 即 merge(p2.l,p1);
    split(p,k)几点记录value,key,l,r,size
    p.L<-p->p.r;
    1.要是k<=p.l.size 说明k小的点全在左子数,递归split(p.l,k);构成新树的时候直接把split后剩下的左子树接到P根上就好了
    2.k=p.l.size+1;,返回两棵树(p.l-p,p.r)
    3.k>p.l.siz+1,左边已经全不要,那么就split(p.r,k-p.l.size-1);
    返回两棵树(p.l-p-p.r,剩余p.r)

    merge:

    int merge(int x,int y) {
        if(!x) return y;//left son is empty
        else if(!y)return x;//right son is empty
        if(key[x]<key[y]) {
            tree[x][1]=merge(tree[x][1],y);//union y_tree to x's right son  
            update(x);
            return x;
        }
        else {
            tree[y][0]=merge(x,tree[y][0]);//union x_tree to y'left son
            update(y);
            return y;
        }
    }
    

    split:

    void split(int now,int k,int &x,int &y) {//根据插入数的val值,把树分为小于等于k的,和大于k的 
        if (!now) x=y=0;//指针为空 
        else {
            if (val[now]<=k)
                x=now,split(tree[now][1],k,tree[now][1],y);//分割标准大于当前节点val访问右子树 
            else
                y=now,split(tree[now][0],k,x,tree[now][0]);//否则访问左子树 
            update(now);
        }
    }
    

    query_min:

    查询那些数比x数小,当找到一个根节点比x小时,那么该节点的所有子树都比他小,那么就把子树size+1加到答案里-->删除一个数的时候时用来确定split的k(比要删除的数小的)值

    query_kth

    查询第k大的数

    int kth(int now,int k) {
        while(1) {
            if (k<=siz[tree[now][0]])//左子树大小比k大说明第k大在左子树中
                now=tree[now][0];
            else
            if (k==siz[tree[now][0]]+1)
                return now;
            else
                k-=siz[tree[now][0]]+1,now=tree[now][1];//在右子树中查询第k-左子树子树个数 大的数
        }
    }
    

    查询数a的rank

    按照权值进行split,split出的x数(所有节点权值小于a)的大小+1,就是a的rank

    split(root,a-1,x,y);
                printf("%d
    ",siz[x]+1);
                root=merge(x,y);
    

    插入一个新节点

    int make_new(int v) {
        siz[++sz]=1;
        val[sz]=v;
        key[sz]=rand();
        return sz;
    }
    

    delite a number

    删除一个数,把这个数split出来,然后将另外两颗split出来的树merge起来

                split(root,a,x,z);
                split(x,a-1,x,y);
                y=merge(tree[y][0],tree[y][1]);
                root=merge(merge(x,y),z);
    

    查询前驱

    查询a的前驱
    查询按照a作为标准split出的x子树中的最大值

    split(root,a-1,x,y);
                printf("%d
    ",val[kth(x,siz[x])]);
                root=merge(x,y);
    

    后继

    查询a的后继
    查询按照a作为标准split出的y子树中的最小值

                split(root,a,x,y);
                printf("%d
    ",val[kth(y,1)]);
                root=merge(x,y);
    

    区间操作

    总结,利用split与merge完成各种操作

  • 相关阅读:
    TensorFlow安装流程(GPU加速)
    个人总结
    第十六周学习进度
    第十五周学习进度
    第十四周学习进度
    第二次冲刺个人工作总结12
    第二次冲刺个人工作总结11
    第二次冲刺个人工作总结10
    软件工程课堂作业——购买《哈利波特》的最低价格
    第二次冲刺个人工作总结09
  • 原文地址:https://www.cnblogs.com/sssy/p/8012504.html
Copyright © 2011-2022 走看看