Treap

树堆Treap

treap是平衡树的一种,简单好写而且速度快,是性价比很高的选择。

目录

算法思想

  1. treap采用了随机化思想,考虑到按照普通二叉搜索树构建时会由于数据而退化,treap在构建时,为每个节点重新随机分配了一个权值。这样构建时,节点就会相对平衡的分布了,所以treap复杂度是期望O(nlogn)
  2. 具体说来,treap正如其名==tree+heap,是二叉搜索树和堆的结合体。对于每个节点的值来说,它是一棵二叉搜索树;而对于每个节点随机分配的权值来说,它又是一个堆(具体是大还是小根堆由个人喜好来定,不过小根堆会有一个小优点,这个稍后说);
  3. treap之所以好写,其实就是因为它除了这一点不同,就是一个普通的二叉搜索树,如果学了splay,会左右旋这个直接写是很容易的。

算法实现

  • 插入

    1. 按照普通的二叉排序树先插入节点,同时为其随机分配一个权值;
    2. 为了维护堆的性质,和该节点的父亲比较,如果不符合条件就上旋;
    3. 通常我们建成小根堆,因为rand()函数值域为[0,32767],而根节点的父亲0号节点的权值没有修改、为0,我们上旋时就无需考虑是否已经转到根节点上了。
  • 删除

    1. 有些类似堆的删除,因为我们无需考虑要删除的节点对treap的影响,所以直接相堆一样把它换到叶节点然后删除即可;
    2. 具体说就是在两个儿子里挑一个权值最优的(大根堆挑最大的,小根堆挑最小的)把它旋上来作为被删节点的替代,如此反复直至被删节点旋转到叶节点,最后把它删除即可。
  • 其它操作

    1. splay基本一样,这也是学完splay后treap好学的原因;
    2. 不过要注意的一点是,splay每次都把新更改的节点旋转到了根节点,所以递推写法也很好更新子树大小,但treap不同,推荐写递归版的,递推版更新真是要死人。
      不过我图zhao省ma事fan,直接按之前写的splay改了,是递推版…

代码

BZOJ-3224 [平衡树三题]普通平衡树(模板)

#include <cstdio>
#include <cstdlib>
using namespace std;

const int MAXN=100005;

struct node{
    int s[2],w,val,cnt,sz,fa;
};

class treap{
    private:
        int sz,cnt,rt;
        node d[MAXN];
        void sgl_upd(const int &u){
            d[u].sz=d[d[u].s[0]].sz+d[d[u].s[1]].sz+d[u].cnt;
        }
        void path_upd(int u){
            while(u){
                sgl_upd(u);
                u=d[u].fa;
            }
        }
        void rot(int u){
            int ufa=d[u].fa;
            bool lr= d[ufa].s[1]==u;
            d[u].fa=d[ufa].fa;
            if(d[ufa].fa)
                d[d[ufa].fa].s[d[d[ufa].fa].s[1]==ufa]=u;
            else rt=u;
            d[ufa].s[lr]=d[u].s[lr^1];
            d[d[ufa].s[lr]].fa=ufa;
            d[u].s[lr^1]=ufa;
            d[ufa].fa=u;
            sgl_upd(ufa),sgl_upd(u);
        }
        void mt(const int &u){
            while(d[u].w<d[d[u].fa].w) rot(u);
        }
        int find(const int &val){
            int u=rt;
            while(u){
                if(d[u].val==val) return u;
                u=d[u].s[val>d[u].val];
            }
            return 0;
        }
    public:
        void ist(const int &val){
            ++sz;
            int u=rt,v=0;
            while(u){
                if(val==d[u].val){
                    ++d[u].cnt;
                    path_upd(u);
                    return;
                }
                v=u;
                u=d[u].s[val>d[u].val];
            }
            u=++cnt;
            d[u].cnt=d[u].sz=1;
            d[u].val=val;
            d[u].w=rand();
            if(v){
                d[u].fa=v;
                d[v].s[val>d[v].val]=u;
                mt(u);
                path_upd(u);
            }
            else rt=u;
        }
        void del(const int &val){
            int u=find(val);
            if(!u) return;
            if(d[u].cnt>1){
                --d[u].cnt;
                path_upd(u);
                return;
            }
            int &ls=d[u].s[0],&rs=d[u].s[1];
            while(ls|rs){
                if(ls && rs)
                    rot(d[u].s[d[rs].w<d[ls].w]);
                else rot(ls|rs);
            }
            d[d[u].fa].s[d[d[u].fa].s[1]==u]=0;
            path_upd(d[u].fa);
            if(--sz==0) rt=0;
        }
        int low_b(const int &val){
            int u=rt,v=0;
            while(u){
                if(d[u].val<val)
                    v=u,u=d[u].s[1];
                else u=d[u].s[0];
            }
            return d[v].val;
        }
        int up_b(const int &val){
            int u=rt,v=0;
            while(u){
                if(d[u].val>val)
                    v=u,u=d[u].s[0];
                else u=d[u].s[1];
            }
            return d[v].val;
        }
        int rank(const int &val){
            int u=rt,s=1;
            while(u){
                if(d[u].val<val){
                    s+=d[d[u].s[0]].sz+d[u].cnt;
                    u=d[u].s[1];
                }
                else u=d[u].s[0];
            }
            return s;
        }
        int get_val(int k){
            int u=rt;
            while(k<=d[d[u].s[0]].sz || d[d[u].s[0]].sz+d[u].cnt<k){
                if(k<=d[d[u].s[0]].sz)
                    u=d[u].s[0];
                else{
                    k-=d[d[u].s[0]].sz+d[u].cnt;
                    u=d[u].s[1];
                }
            }
            return d[u].val;
        }
}T;

int main(){
    int n;
    scanf("%d",&n);
    for(int i=1,opt,x;i<=n;++i){
        scanf("%d%d",&opt,&x);
        switch(opt){
            case 1: T.ist(x); break;
            case 2: T.del(x); break;
            case 3: printf("%d\n",T.rank(x)); break;
            case 4: printf("%d\n",T.get_val(x)); break;
            case 5: printf("%d\n",T.low_b(x)); break;
            default:printf("%d\n",T.up_b(x));
        }
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值