非旋Treap

Posted genius777

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了非旋Treap相关的知识,希望对你有一定的参考价值。

最近看到有一种不用旋转的treap,好像还可以持久化,于是就学了一下。

如果你还不会Treap的话,那你可以点击这里,对旋转Treap有个大致了解,这里就不赘述Treap的性质了。

treap就是tree+heap。它的每个节点的权值data满足排序二叉树的性质,随机权值key满足堆的性质。由于key是随机的所以它大致是平衡的。
不基于旋转的treap有两个基本操作:

merge(a,b):返回一个treap,包含a,b两个treap中的所有节点,但要保证b中所有节点权值都大于等于a。

split(a,n)返回两个treap l,r。其中l中包含treap a中的前n个节点,r中包含treap a中的剩余节点。

这两个操作的实现都很简单(这里我们维护小根堆的性质):

merge(a,b):若a的key< b的key则将a的右儿子变为merge(a的右儿子,b)。
否则将b的左儿子变为merge(a,b的左儿子)。

split(a,n):若a左子树的size(记为m)=n则返回a的左子树,a和a的右子树。若m=n-1则返回a的左子树和a,a的右子树。否则若m>n则设{l,r}为split(a的左子树,n)将a的左子树设为r,返回l,a。若m< n-1则设{l,r}为split(a的右子树,n-m-1)将a的右儿子设为l,返回a,r。

有了这两操作我们就可以实现插入和删除了。

插入x:找到x所在位置,将其split开,在合并l,x与x,r。

删除x:找到x的位置,将x与其前后位置都split开,在合并另外两部份。
其余操作和不同平衡树一样。


【代码实现】

  1 #include<cstdio>
  2 #include<algorithm>
  3 #define maxn 100010
  4 #define mp make_pair
  5 using namespace std;
  6 typedef pair<int,int> pp;
  7 struct node{
  8     int son[2],val,rd,size;
  9 }t[maxn];
 10 int op,n,root,cnt; 
 11 const int INF=1e9+7;
 12 void updata(int v){t[v].size=t[t[v].son[0]].size+t[t[v].son[1]].size+1;}
 13 pp split(int v,int k)
 14 {
 15     if(k==0)return mp(0,v);
 16     int ls=t[v].son[0],rs=t[v].son[1];
 17     if(t[ls].size==k) {t[v].son[0]=0,updata(v);return mp(ls,v);}
 18     if(t[ls].size+1==k) {t[v].son[1]=0,updata(v);return mp(v,rs);}
 19     if(t[ls].size>k)
 20     {
 21         pp tmp=split(ls,k);
 22         t[v].son[0]=tmp.second,updata(v);
 23         return mp(tmp.first,v);
 24     }
 25     pp tmp=split(rs,k-t[ls].size-1);
 26     t[v].son[1]=tmp.first,updata(v);
 27     return mp(v,tmp.second);
 28 }
 29 int merge(int x,int y)
 30 {
 31     if(!x||!y)return x+y;
 32     if(t[x].rd<t[y].rd) {t[x].son[1]=merge(t[x].son[1],y),updata(x);return x;}
 33     else {t[y].son[0]=merge(x,t[y].son[0]),updata(y);return y;}
 34 }
 35 int rank(int v,int k)
 36 {
 37     int ans=0,tmp=INF;
 38     while(v)
 39     {
 40         if(k==t[v].val)tmp=min(tmp,ans+t[t[v].son[0]].size+1);
 41         if(k>t[v].val)ans+=t[t[v].son[0]].size+1,v=t[v].son[1];
 42         else v=t[v].son[0];
 43     }
 44     return tmp==INF?ans:tmp;
 45 }
 46 int find(int v,int k)
 47 {
 48     while(1)
 49     {
 50         if(t[t[v].son[0]].size+1==k)return t[v].val;
 51         if(t[t[v].son[0]].size>=k)v=t[v].son[0];
 52         else k=k-t[t[v].son[0]].size-1,v=t[v].son[1];
 53     }
 54 }
 55 int pre(int v,int k)
 56 {
 57     int ans=-INF;
 58     while(v)
 59     {
 60         if(t[v].val<k)ans=max(ans,t[v].val),v=t[v].son[1];
 61         else v=t[v].son[0];
 62     }
 63     return ans;
 64 }
 65 int next(int v,int k)
 66 {
 67     int ans=INF;
 68     while(v)
 69     {
 70         if(t[v].val>k)ans=min(ans,t[v].val),v=t[v].son[0];
 71         else v=t[v].son[1];
 72     }
 73     return ans;
 74 }
 75 void insert(int v)
 76 {
 77     int k=rank(root,v);
 78     pp tmp=split(root,k);
 79     t[++cnt].val=v;
 80     t[cnt].rd=rand();
 81     t[cnt].size=1;
 82     root=merge(tmp.first,cnt);
 83     root=merge(root,tmp.second);
 84 }
 85 void del(int v)
 86 {
 87     int k=rank(root,v);
 88     pp tmp1=split(root,k);
 89     pp tmp2=split(tmp1.first,k-1);
 90     root=merge(tmp2.first,tmp1.second);
 91 }
 92 int main()
 93 {
 94     int x;
 95     scanf("%d",&n);
 96     for(int i=1;i<=n;i++)
 97     {
 98         scanf("%d%d",&op,&x);
 99         if(op==1)insert(x);
100         else if(op==2)del(x);
101         else if(op==3)printf("%d
",rank(root,x));
102         else if(op==4)printf("%d
",find(root,x));
103         else if(op==5)printf("%d
",pre(root,x));
104         else printf("%d
",next(root,x));
105     }
106     return 0;
107 }

 

以上是关于非旋Treap的主要内容,如果未能解决你的问题,请参考以下文章

沉迷数据结构1(treap&非旋treap)

非旋Treap总结 : 快过Splay 好用过传统Treap

非旋Treap

非旋treap

非旋 treap 结构体数组版(无指针)详解,有图有真相

非旋 treap 结构体数组版(无指针)详解,有图有真相