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 }
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 }
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 }
這兩個操作是與堆流程類似的操作(雖然作為c++黨我不寫堆);
還有尋找Kth number,尋找rank,尋找last、next等等與平衡樹相關的操作,不在此處贅述;
局限性:不能完成區間操作;
有關代碼:

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
非旋轉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<r,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 }
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 }
insert(value):把樹split成A樹≤value,B樹>value,然后建一個value的點x,然后merge(root,A,x),merge(root,root,B);
del(value):把樹拆成三部分,中間是等於value的點——把這部分的點減少一個,然后再合並樹的三部分;
非旋轉treap是一種非常好的平衡樹,她有treap的優良性質——常數比Splay小,而且還支持區間操作和可持久化,
重點是代碼短
唯一的缺點的是不好理解
有關代碼:

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<r,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 }