伸展树基础(Splay)

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 }

 

你可能感兴趣的:(play)