旋轉/非旋轉treap的簡單操作


treap(樹堆)

是在二叉搜索樹的基礎上,通過維護隨機附加域,使其滿足堆性質,從而使樹相對平衡的二叉樹;

為什么可以這樣呢?

因為在維護堆的時候可以同時保證搜索樹的性質;

(比如當一棵樹的一個域滿足堆的性質時,只要不斷的互換左右,她的另一個域總會滿足搜索樹的性質)

(當一棵樹的一個域滿足搜索樹的性質時,只要不斷的上下旋轉,她的另一個域總會滿足二叉堆的性質)

於是樹堆有兩種實現:

旋轉treap:

她的操作基於旋轉;

注意:當存在相同val時,對於旋轉treap,是存在一個節點中的;

roll(旋轉操作)

交換點x與她的x.ch(i):

fa(x).ch(i)=x.ch(i);

x.ch(i)=x.ch(i).ch(i^1);

x.ch(i).ch(i^1)=x;

以上操作是一個改變上下位置卻不影響搜索樹性質的方法,與splay類似;

1 void roll(int &now){
2     int wh=data[data[now].ch[0]].key<data[data[now].ch[1]].key?0:1;
3     int son=data[now].ch[wh];
4     data[now].ch[wh]=data[son].ch[wh^1];
5     data[son].ch[wh^1]=now;
6     up(now);
7     now=son;
8 }
View Code

insert(插入操作)

普通的搜索樹的插入操作,是插入一個葉子;

普通的堆也是插入一個葉子,然后再把她旋轉到相應的位置上;

這樣的話,只要按搜索樹的法則插入一個節點,然后再以不影響搜索樹性質的方式把她旋轉到符合堆性質的位置即可;

 1 void insert(int &now){
 2     if(now==0){
 3         now=make_data(x);
 4         return;
 5     }
 6     if(data[now].value==x){
 7         data[now].cnt++;
 8         data[now].size++;
 9     }
10     else{
11         int wh=x < data[now].value ? 0 : 1;
12         insert(data[now].ch[wh]);
13         if(data[now].key>=data[data[now].ch[wh]].key)
14             roll(now);
15     }
16     up(now);
17 }
View Code

del(刪除操作)

與堆類似的;

把她通過旋轉操作下沉到葉子節點,然后再斷開即可;

 1 void del(int &now){
 2     if(data[now].value==x){
 3         if(data[now].cnt==1){
 4             if(data[now].ch[0]*data[now].ch[1]==0){
 5                 now=data[now].ch[1]+data[now].ch[0];
 6                 return ;
 7             }
 8             roll(now);
 9             int wh=data[data[now].ch[0]].value==x?0:1;
10             del(data[now].ch[wh]);
11         }
12         else{
13             data[now].size--;    data[now].cnt--;
14         }
15     }
16     else{
17         int wh=data[now].value>x?0:1;
18         del(data[now].ch[wh]);
19     }
20     up(now);
21 }
View Code

這兩個操作是與堆流程類似的操作(雖然作為c++黨我不寫堆);

還有尋找Kth number,尋找rank,尋找last、next等等與平衡樹相關的操作,不在此處贅述;

局限性:不能完成區間操作;

有關代碼:

洛谷P3369 【模板】普通平衡樹

  1 #include<cstdio>
  2 #include<cstdlib>
  3 using namespace std;
  4 #define INF 2147483647
  5 int n;
  6 struct poo
  7 {
  8     int size,value,key,cnt;
  9     int ch[2];
 10 }data[100001];
 11 int tot,root,x;
 12 int  make_data(int );
 13 void insert(int&);
 14 void roll(int&);
 15 int  find(    );
 16 int  rank(    );
 17 void del(int&);
 18 int  las(int );
 19 int  nex(int );
 20 void up(int );
 21 int main()
 22 {
 23     int i,j;
 24     data[0].value=INF;data[0].key=INF;
 25     scanf("%d",&n);
 26     for(i=1;i<=n;i++){
 27         scanf("%d%d",&j,&x);
 28         switch(j){
 29             case 1:insert(root);break;
 30             case 2:   del(root);break;
 31             case 3:  printf("%d\n",rank(    ));break;
 32             case 4:  printf("%d\n",find(    ));break;
 33             case 5:  printf("%d\n", las(root));break;
 34             case 6:  printf("%d\n", nex(root));break;
 35         }
 36     }
 37     return 0;
 38 }
 39 int make_data(int value){
 40     tot++;
 41     data[tot].cnt++;
 42     data[tot].key=(rand()/2+rand()/2);
 43     data[tot].size=1;
 44     data[tot].value=value;
 45     return tot;
 46 }
 47 void insert(int &now){
 48     if(now==0){
 49         now=make_data(x);
 50         return;
 51     }
 52     if(data[now].value==x){
 53         data[now].cnt++;
 54         data[now].size++;
 55     }
 56     else{
 57         int wh=x < data[now].value ? 0 : 1;
 58         insert(data[now].ch[wh]);
 59         if(data[now].key>=data[data[now].ch[wh]].key)
 60             roll(now);
 61     }
 62     up(now);
 63 }
 64 void roll(int &now){
 65     int wh=data[data[now].ch[0]].key<data[data[now].ch[1]].key?0:1;
 66     int son=data[now].ch[wh];
 67     data[now].ch[wh]=data[son].ch[wh^1];
 68     data[son].ch[wh^1]=now;
 69     up(now);
 70     now=son;
 71 }
 72 int find(){
 73     int now=root;
 74     int ls,rs;
 75     ls=data[now].ch[0];rs=data[now].ch[1];
 76     while(x<=data[ls].size||x>data[now].size-data[rs].size){
 77         if(data[ls].size>=x)
 78             now=ls;
 79         else{
 80             x=x+data[rs].size-data[now].size;
 81             now=rs;
 82         }
 83         ls=data[now].ch[0];rs=data[now].ch[1];
 84     }
 85     return data[now].value;
 86 }
 87 int rank(){
 88     int now=root,ans=0;
 89     int ls=data[now].ch[0],rs=data[now].ch[1];
 90     while(x!=data[now].value&&x!=0)
 91     {
 92         if(x<data[now].value)
 93             now=ls;
 94         else{
 95             ans+=data[now].size-data[rs].size;
 96             now=rs;
 97         }
 98         ls=data[now].ch[0];rs=data[now].ch[1];
 99     }
100     return ans+data[ls].size+1;
101 }
102 void del(int &now){
103     if(data[now].value==x){
104         if(data[now].cnt==1){
105             if(data[now].ch[0]*data[now].ch[1]==0){
106                 now=data[now].ch[1]+data[now].ch[0];
107                 return ;
108             }
109             roll(now);
110             int wh=data[data[now].ch[0]].value==x?0:1;
111             del(data[now].ch[wh]);
112         }
113         else{
114             data[now].size--;    data[now].cnt--;
115         }
116     }
117     else{
118         int wh=data[now].value>x?0:1;
119         del(data[now].ch[wh]);
120     }
121     up(now);
122 }
123 int las(int now){
124     int ans=0,an=0;
125     if(!now)return 0;
126     if(data[now].value<x){
127         ans=data[now].value;
128         an=las(data[now].ch[1]);
129         ans=an!=0?an:ans;
130     }
131     else{
132         ans=las(data[now].ch[0]);
133     }
134     return ans;
135 }
136 int nex(int now){
137     int ans=0,an=0;
138     if(!now)return 0;
139     if(data[now].value>x){
140         ans=data[now].value;
141         an=nex(data[now].ch[0]);
142         ans=an!=0?an:ans;
143     }
144     else{
145         ans=nex(data[now].ch[1]);
146     }
147     return ans;
148 }
149 void up(int now){
150     data[now].size=data[data[now].ch[0]].size+data[data[now].ch[1]].size+data[now].cnt;
151 }
152 //treap on the 2017.1.21
153 //10
154 //1 5
155 //4 1
156 //1 6
157 //1 7
158 //1 10
159 //1 3
160 //1 4
161 //6 2
162 //1 8
163 //5 9
164 //
165 //14
166 //1 5
167 //1 6
168 //1 7
169 //1 10
170 //1 3
171 //1 4
172 //1 8
173 //3 3
174 //3 4
175 //3 5
176 //3 6
177 //4 5
178 //4 6
179 //4 7
View Code

非旋轉treap

同樣是樹堆,旋轉treap通過與堆類似的旋轉操作維護,但非旋轉treap,通過與平衡樹類似的拆分|合並操作完成;

注意:當存在相同val時,對於非旋轉treap,是存在不同節點中的;

split(拆分操作)

這里介紹按value拆分(還有按排名拆分不講)

首先預留兩個變量名作為兩棵樹的樹根名;

然后從原樹根開始,按查找value為k的點的方法往下走,對於每一個走到的點,她大於value則屬於右樹,否則屬於左樹,

因為是從上往下走的,所以后進入樹的點是先入者的子節點(符合堆性質)

因為當x.val>value時下一步走x.ls,之后的點全比x小,所以把x接到右樹后,下一個接入右樹的點——不管是誰——應當變成x的左兒子;

對x.val≤value,也是相似的;

 1 void split(int now,int&ltr,int&rtr,int value){
 2     if(!now){
 3         ltr=rtr=0;
 4         return;
 5     }
 6     if(data[now].val<=value)
 7         ltr=now,split(data[now].ch[1],data[ltr].ch[1],rtr,value);
 8     else
 9         rtr=now,split(data[now].ch[0],ltr,data[rtr].ch[0],value);
10     up(now);
11 }
View Code

merge(合並操作)

這里介紹當treapA的所有節點val小於treapB時的操作(即通常使用的操作)

可以看出只要把A的右鏈和B的左鏈,拆開,按符合堆上下順序排好,再連上符合搜索樹的父子關系即可;

因為AB分別滿足堆性質,所以所謂的“按符合堆上下順序排好”,只要互相參差插入即可,不改變A,B各自的相對上下順序;

連父子關系時,屬於A樹的點需要連一個rs(因為排在她下方的點都比她大)屬於B樹的點需要連一個ls(因為排在她下方的點都比她小)

實現是遞歸的

merge( &now , a , b )-->( a.key < b.key ? ( now=a.rs , merge( &now.rs , a.rs , b ) ) : ( now=b.ls , merge( &now.ls , a , b.ls ) ) );

 1 void merge(int&now,int s,int b){
 2     if(!s||!b){
 3         now=s+b;return;
 4     }
 5     if(data[s].key<data[b].key)
 6         now=s,merge(data[now].ch[1],data[s].ch[1],b);
 7     else
 8         now=b,merge(data[now].ch[0],s,data[b].ch[0]);
 9     up(now);
10 }
View Code

insert(value):把樹split成A樹≤value,B樹>value,然后建一個value的點x,然后merge(root,A,x),merge(root,root,B);

del(value):把樹拆成三部分,中間是等於value的點——把這部分的點減少一個,然后再合並樹的三部分;

非旋轉treap是一種非常好的平衡樹,她有treap的優良性質——常數比Splay小,而且還支持區間操作和可持久化,

重點是代碼短

唯一的缺點的是不好理解

有關代碼:

洛谷P3369 【模板】普通平衡樹

  1 #include<cstdio>
  2 #include<cstdlib>
  3 using namespace std;
  4 const int INF=2147483600;
  5 struct Treap{
  6     int val,key,size;
  7     int ch[2];
  8 }data[200010];
  9 int root,tot;
 10 void make_data(int&,int );
 11 void up(int );
 12 void merge(int&,int,int);
 13 void split(int ,int&,int&,int );
 14 void insert(int );
 15 void del(int );
 16 void rank(int );
 17 void find(int ,int );
 18 int  las(int );
 19 int  nex(int );
 20 int main()
 21 {
 22     srand(2.17);
 23     int i,j,k,m;
 24     root=0;data[0].key=INF;data[0].val=INF;data[0].size=0;
 25     scanf("%d",&m);
 26     for(i=1;i<=m;i++){
 27         scanf("%d%d",&j,&k);
 28         if(j==1)insert(k);
 29         if(j==2)del(k);
 30         if(j==3)rank(k);
 31         if(j==4)find(root,k);
 32         if(j==5)las(k);
 33         if(j==6)nex(k);
 34     }
 35     return 0;
 36 }
 37 void make_data(int&now,int value){
 38     data[++tot].val=value;data[tot].key=rand();
 39     data[tot].size=1;
 40     data[tot].ch[0]=data[tot].ch[1]=0;
 41     now=tot;
 42 }
 43 void up(int now){
 44     data[now].size=data[data[now].ch[0]].size+data[data[now].ch[1]].size+1;
 45 }
 46 void merge(int&now,int s,int b){
 47     if(!s||!b){
 48         now=s+b;return;
 49     }
 50     if(data[s].key<data[b].key)
 51         now=s,merge(data[now].ch[1],data[s].ch[1],b);
 52     else
 53         now=b,merge(data[now].ch[0],s,data[b].ch[0]);
 54     up(now);
 55 }
 56 void split(int now,int&ltr,int&rtr,int value){
 57     if(!now){
 58         ltr=rtr=0;
 59         return;
 60     }
 61     if(data[now].val<=value)
 62         ltr=now,split(data[now].ch[1],data[ltr].ch[1],rtr,value);
 63     else
 64         rtr=now,split(data[now].ch[0],ltr,data[rtr].ch[0],value);
 65     up(now);
 66 }
 67 void insert(int value){
 68     int x=0,y=0,z=0;
 69     make_data(z,value);
 70     split(root,x,y,value);
 71     merge(x,x,z);
 72     merge(root,x,y);
 73 }
 74 void del(int value){
 75     int x=0,y=0,z=0;
 76     split(root,x,y,value);
 77     split(x,x,z,value-1);
 78     merge(z,data[z].ch[0],data[z].ch[1]);
 79     merge(x,x,z);merge(root,x,y);
 80 }
 81 void rank(int value){
 82     int x=0,y=0;
 83     split(root,x,y,value-1);
 84     printf("%d\n",data[x].size+1);
 85     merge(root,x,y);
 86 }
 87 void find(int now,int x){
 88     while(data[data[now].ch[0]].size+1!=x){
 89         if(data[data[now].ch[0]].size>=x)
 90             now=data[now].ch[0];
 91         else
 92             x-=(data[data[now].ch[0]].size+1),now=data[now].ch[1];
 93     }
 94     printf("%d\n",data[now].val);
 95 }
 96 int  las(int value){
 97     int x=0,y=0;
 98     split(root,x,y,value-1);
 99     find(x,data[x].size);
100     merge(root,x,y);
101 }
102 int  nex(int value){
103     int x=0,y=0;
104     split(root,x,y,value);
105     find(y,1);
106     merge(root,x,y);
107 }
View Code

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM