【数据结构】Treap——方便的平衡树

来源:互联网 发布:买空卖空的淘宝包邮 编辑:程序博客网 时间:2024/06/14 21:47

前言

顾名思义,treap就是tree+Heap,复杂度与Splay的均摊log不同,treap是期望log,但与Splay比起来,功能都差不多,但代码的长度和调试难度都比Spaly要舒服很多,

简介

treap是一棵二叉查找树,与普通的二叉查找树不同,对于每个节点,它还记录一个随机值rd,满足,如果只看rd的话,它就是一个堆,
这样,它的期望深度就是log的,每次的查询修改直接在上面跑也没问题。

以下为treap的基本操作,各种在这上面扩展出的其他的操作这里就不细讲了

Build

我们可以发现一棵treap同时也是一棵迪笛卡尔树,
那么建树就可以O(n)解决了,用一个栈即可,
(当然你像插入一样做也没问题)

//b[i].rd为位置i的随机值//merge()为标记的合并,void build(int q){    if(b[q].l)build(b[q].l);    if(b[q].r)build(b[q].r);    merge(q);}void build_treap(){    int la,w;    n++;b[n].rd=2e9;    fo(i,1,n)    {        int la=0;        for(;za[0]&&b[za[za[0]]].rd<b[i].rd;za[0]--)        {            la=w=za[za[0]];            if(za[0]-1)b[w].fa=za[za[0]-1],b[za[za[0]-1]].r=w;        }        if(i>n)break;        b[i].l=la;        b[la].fa=i;        za[++za[0]]=i;    }    root=n;    build(root);}

Split

treap当然离不开Split操作啦,
这个点操作的作用是把treap两棵treap,一半为前si个,另一半就是剩下的,
具体的操作是:判断断开的位置是在左子树还是右子树,再递归求解,
(看标的话理解的会快一点)

//merge()为标记的合并,typedef pair<int,int> TRP;TRP split(int q,int si){    if(!si)return TRP(0,q);    TRP t;    if(si<=b[b[q].l].si)    {        t=split(b[q].l,si);        b[q].l=t.second;        t.second=q;    }else    {        t=split(b[q].r,si-1-b[b[q].l].si);        b[q].r=t.first;        t.first=q;    }    merge(q);    return t;}

Merge

合并就简单了,直接把较大的那个放到这个位置即可

//因为merge函数被标记合并占用了,所以只能用另一个词了int amalgamate(int q,int w){    if(!q||!w)return q+w;    if(b[q].rd>b[w].rd)    {        b[q].r=amalgamate(b[q].r,w);        merge(q);        return q;    }    b[w].l=amalgamate(q,b[w].l);    merge(w);    return w;}
原创粉丝点击