码迷,mamicode.com
首页 > 其他好文 > 详细

伸展树基础(Splay)

时间:2015-07-07 12:54:33      阅读:250      评论:0      收藏:0      [点我收藏+]

标签:

3224: Tyvj 1728 普通平衡树

Time Limit: 10 Sec  Memory Limit: 128 MB
Submit: 3948  Solved: 1627
[Submit][Status][Discuss]

Description

您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作:
1. 插入x数
2. 删除x数(若有多个相同的数,因只删除一个)
3. 查询x数的排名(若有多个相同的数,因输出最小的排名)
4. 查询排名为x的数
5. 求x的前驱(前驱定义为小于x,且最大的数)
6. 求x的后继(后继定义为大于x,且最小的数)

Input

第一行为n,表示操作的个数,下面n行每行有两个数opt和x,opt表示操作的序号(1<=opt<=6)

Output

对于操作3,4,5,6每行输出一个数,表示对应答案

Sample Input

10

1 106465

4 1

1 317721

1 460929

1 644985

1 84185

1 89851

6 81968

1 492737

5 493598
 

Sample Output

106465

84185

492737
 

HINT

 

1.n的数据范围:n<=100000

2.每个数的数据范围:[-1e7,1e7]

 

以下代码是模板式题解,并有详细注释。。。

  1 #include<cstdio>
  2 #include<cstdlib>
  3 #include<iostream>
  4 #include<algorithm>
  5 #include<cmath>
  6 #include<cstring>
  7 #include<queue>
  8 using namespace std;
  9 struct splay
 10 {
 11     int data,lc,rc,fa;
 12     int size;//子树的点,包括自己 
 13 }a[100005];
 14 
 15 int q,root=0,tot=0;
 16 
 17 void update(int x){//算x点的size值 
 18     a[x].size=a[a[x].lc].size+a[a[x].rc].size+1;//注意:一定要+1,即算上自己本身 
 19 }
 20 
 21 void r_rotate(int x){//让x右旋 
 22     /*
 23     右旋:
 24     条件:右旋出现在x,y之间,x是y的左儿子
 25     目标:x变为父亲,y变为儿子(一定是右儿子) 
 26     步骤:
 27     1.如果x有有儿子,使y的左儿子(原来是x)变成x的右儿子(现在x的右儿子是y)
 28     2.x的父亲变成y原来的父亲,并更新父亲指针
 29     3.更新x与y的父亲儿子指针和size值 
 30     */
 31     int y=a[x].fa;//y是x的父亲 
 32     //步骤1: 
 33     a[y].lc=a[x].rc;//换儿子
 34     if(a[x].rc!=0)//如果x的原右儿子不为空:
 35         a[a[x].rc].fa=y;//使x的原右儿子父亲为y
 36     //步骤2:
 37     a[x].fa=a[y].fa;//换父亲
 38     if (y==a[a[y].fa].lc)//如果y是其父亲的左儿子 
 39         a[a[y].fa].lc=x;
 40     else//如果y是其父亲的右儿子 
 41         a[a[y].fa].rc=x;
 42     //步骤3:
 43     a[y].fa=x;
 44     a[x].rc=y;//注意:右旋y一定变成x的右儿子 
 45     update(y);//更新y的size 
 46     update(x);//更新x的size 
 47 }
 48 void l_rotate(int x){//左旋方式同右旋 
 49     int y=a[x].fa;
 50     a[y].rc=a[x].lc;
 51     if (a[x].lc!=0) a[a[x].lc].fa=y;
 52     a[x].fa=a[y].fa;
 53     if (y==a[a[y].fa].lc) a[a[y].fa].lc=x;
 54     else a[a[y].fa].rc=x;
 55     a[y].fa=x;
 56     a[x].lc=y;
 57     update(y);
 58     update(x);
 59 }
 60 void splay(int x,int s){//将x旋到s的下方
 61     /**/
 62     while (a[x].fa!=s){
 63         if (x==a[a[x].fa].lc)//如果x是其父亲的左儿子 
 64             r_rotate(x);//右旋 
 65         else//如果x是其父亲的右儿子 
 66             l_rotate(x);//左旋 
 67     }
 68     update(x);//旋之后更新x的size 
 69     if (s==0)//将x旋到0的下方,x变成了根 
 70         root=x;
 71 }
 72 
 73 int Search(int w){//要插入点的data
 74     int p,x=root;
 75     while(x){
 76         p=x;
 77         //二叉搜索树性质:对于任意节点x,其左子树节点的data小于x的data
 78         //                               其右子树节点的data大于x的data 
 79         if (a[x].data>w)//当前节点data大于要插入的data,所以要插入的一定在左子树 
 80         x=a[x].lc;
 81         
 82         else x=a[x].rc;//否则在右子树 
 83     }
 84     return p;
 85 }
 86 
 87 void New_Node(int &x,int fa,int key){//对于一个新节点x,使其左右孩子都为0,父亲
 88                      //为fa,节点值为key 
 89     x=++tot;//使root=tot
 90     a[x].lc=a[x].rc=0;
 91     a[x].fa=fa;
 92     a[x].data=key;
 93 }
 94 void Insert(int w)//插入操作 插入一个data为w的节点,使其满足搜索二叉树的性质 
 95 {
 96     if (root==0)//说明没有根节点,插入根节点 
 97     {
 98         New_Node(root,0,w);
 99         return;
100     }
101     //如果不是根节点 
102     int i=Search(w);//找到要插到的父亲节点 
103     if (w<a[i].data)//要插入的节点的data比其父节点小,所以作为左而子 
104         New_Node(a[i].lc,i,w);
105         //否则作为右儿子 
106     else New_Node(a[i].rc,i,w);
107     
108         splay(tot,0);//将当前新节点旋转至0的下方,即旋转至根(保证中序遍历不变) 
109 }
110 int Get(int w){//寻找一个data为w的节点,利用二叉搜索树:右子树.data>节点.data>左子树.data 
111     int x=root;
112     int ans=tot+1;
113     while(x!=0){
114         if(a[x].data>w){
115         x=a[x].lc;
116         continue;
117         }
118         if(a[x].data<w){
119         x=a[x].rc;
120         continue;
121         }
122         if(a[x].data==w){
123             ans=x;
124             x=a[x].lc;
125         }
126     }
127     if (ans==tot+1) return -1;
128     return ans;
129 }
130 
131 int Getmax(int x){//返回以x为根的树的最大data的节点号 
132     while(a[x].rc!=0)
133         x=a[x].rc;
134     return x;
135 }
136 
137 int Getmin(int x){//返回以x为根的树的最小data的节点号 
138     while(a[x].lc!=0)
139         x=a[x].lc;
140     return x;
141 }
142 
143 int Getpre(int x){
144     return Getmax(a[root].lc);
145 }
146 
147 int Getne(int x){
148     return Getmin(a[root].rc);
149 }
150 
151 void Delet(int w){//删除操作 删除一个data为w的节点,使其满足搜索二叉树的性质 
152 /* 
153     步骤: 
154         1.将要删除的节点移至根。
155         2.查找L的最大节点,此时,L的根没有右子树。
156         3.查找R的最小节点 
157         4.删除根,剩下两个子树L(左子树)和R(右子树)。
158 使R成为L的根的右子树。
159 */
160 //    步骤1: 
161     int x=Get(w);
162     splay(x,0);//旋至根
163     //步骤2: 
164     int pp=Getpre(x);
165     //步骤3: 
166     int nn=Getne(x);
167     
168     splay(pp,0);//把以x左孩子为根的最大值节点旋至树根
169     splay(nn,root);//把以x右孩子为根的最小值节点旋至树根下面 
170     //步骤4: 
171     int y=a[x].fa;
172     a[x].fa=0;
173     if (x==a[y].lc) a[y].lc=0;
174     else a[x].lc=0;
175     update(y);
176     update(root);
177 }
178 
179 int Find(int w){//返回比w小的节点的个数 
180     int x=Get(w);
181     splay(x,0);
182     return a[a[x].lc].size;
183 }
184 
185 int Findkth(int x,int k){//在以x节点为根的树中,找第k大 
186     int s=a[a[x].lc].size;
187     if (k==s+1) return a[x].data;
188     if (s>=k) return Findkth(a[x].lc,k);
189     else return Findkth(a[x].rc,k-s-1);
190 }
191 
192 int getpre(int w){
193     int y=Get(w);
194     Insert(w);
195     if (y!=-1) splay(y,0);
196     int ans=Getmax(a[root].lc);
197     Delet(w);
198     return a[ans].data;
199 }
200 
201 int getne(int w){
202     Insert(w);
203     int ans=Getmin(a[root].rc);
204     Delet(w);
205     return a[ans].data;
206 }
207 int main(){
208     root=tot=0;
209     Insert(-50000000);
210     Insert(50000000);
211     scanf("%d",&q);//操作次数 
212     while (q--){
213         int x,k;
214         scanf("%d%d",&x,&k);
215         if (x==1) Insert(k);
216         else if (x==2) Delet(k);
217         else if (x==3) printf("%d\n",Find(k));
218         else if (x==4) printf("%d\n",Findkth(root,k+1));
219         else if (x==5) printf("%d\n",getpre(k));
220         else if (x==6) printf("%d\n",getne(k));
221     }
222     return 0;
223 }

 

伸展树基础(Splay)

标签:

原文地址:http://www.cnblogs.com/CXCXCXC/p/4626599.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!