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

Splay Tree 模板

时间:2016-08-17 10:21:51      阅读:210      评论:0      收藏:0      [点我收藏+]

标签:

  1 /******************************************
  2 数据结构:
  3 Splay_Tree,伸展树;
  4 
  5 详见我的另一篇博客 http://www.cnblogs.com/wyj-jenny/p/5771561.html
  6 
  7 性质:
  8 伸展树是二叉查找树的一种改进;
  9 与二叉查找树一样,伸展树也具有有序性;
 10 即伸展树中的每一个节点x都满足:
 11 该节点左子树中的每一个元素都小于x;
 12 而其右子树中的每一个元素都大于x;
 13 与普通二叉查找树不同的是,伸展树可以自我调整;
 14 
 15 特点:
 16 伸展树并不是严格意义上的平衡树;
 17 也还是极有可能退化成线性结构,但伸展操作能使它的每一次操作近似(logn);
 18 
 19 伸展操作:
 20 伸展操作和平衡树的保持平衡是类似的;
 21 只不过他不要求保持平衡,只是相应的旋转;
 22 旋转有三种情况要处理:
 23 (1)Zig或Zag(节点x的父节点y是根节点)
 24 (2)Zig-Zig或Zag-Zag(节点x的父节点y不是根节点,且x与y同时是各自父节点的左孩子或者同时是各自父节点的右孩子)
 25 (3)Zig-Zag或Zag-Zig(节点x的父节点y不是根节点,x与y中一个是其父节点的左孩子而另一个是其父节点的右孩子)
 26 即一字型旋转和之字型旋转;
 27 
 28 优势:
 29 能快速定位一个区间[l,r],并且能将区间进行删除、旋转操作;
 30 将第l-1个结点旋转至根(之前的Splay操作),将第r+1个结点旋转至根的右孩子;
 31 由于伸展树的本质还是二叉搜索树,则根据二叉查找树的性质可以知道;
 32 在这两个结点之间,也是根的右孩子的左子树就包括节点[l,r];
 33 即很快定位了区间[l,r],如果需要删除,直接把子树拿走即可;
 34 
 35 算法测试:
 36 bzoj 3212 [PKU3468(A Simple Problem with Integers)]
 37 http://www.lydsy.com/JudgeOnline/problem.php?id=3212
 38 
 39 题目大意:
 40 Q a b   :查询区间[a,b]的和;
 41 C a b x : 更新区间[a,b],区间所有值加上x;
 42 *******************************************/
 43 #include<iostream>
 44 #include<cstring>
 45 #include<cstdio>
 46 #include<algorithm>
 47 using namespace std;
 48 
 49 #define Key_value ch[ch[root][1]][0]//进行各种操作的区间
 50 
 51 const int INF=0xffffff;
 52 const int N=100010;
 53 typedef long long LL;
 54 
 55 int ch[N][2];//左右孩子(0为左孩子,1为右孩子)
 56 int pre[N];//父结点
 57 int key[N];//数据域
 58 int size[N];//树的规模
 59 int val[N];
 60 int add[N];
 61 int a[N];//结点元素
 62 LL sum[N];//子树结点和
 63 int root;  //根结点
 64 int tot;//结点数量
 65 int n,q;
 66 
 67 void Push_Up(int u)//通过孩子结点更新父结点
 68 {
 69     size[u]=size[ch[u][0]]+size[ch[u][1]]+1;
 70     sum[u]=sum[ch[u][0]]+sum[ch[u][1]]+val[u]+add[u];
 71 }
 72 
 73 void Push_Down(int u)//将延迟标记更新到孩子结点
 74 {
 75     if(add[u])
 76     {
 77         val[u]+=add[u];
 78         add[ch[u][0]]+=add[u];
 79         add[ch[u][1]]+=add[u];
 80         sum[ch[u][0]]+=(LL)add[u]*size[ch[u][0]];
 81         sum[ch[u][1]]+=(LL)add[u]*size[ch[u][1]];
 82         add[u]=0;
 83     }
 84 }
 85 
 86 void New_Node(int &u,int f,int c)//新建一个结点,f为父节点
 87 {
 88     u=++tot;
 89     val[u]=sum[u]=c;
 90     pre[u]=f;
 91     size[u]=1;
 92     ch[u][1]=ch[u][0]=add[u]=0;
 93 }
 94 
 95 void Build_Tree(int &u,int l,int r,int f)//建树,中间结点先建立,然后分别对区间两端在左右子树建立
 96 {
 97     if(l>r)
 98         return;
 99     int m=(l+r)>>1;
100     New_Node(u,f,a[m]);
101     if(l<m)
102         Build_Tree(ch[u][0],l,m-1,u);
103     if(r>m)
104         Build_Tree(ch[u][1],m+1,r,u);
105     Push_Up(u);
106 }
107 
108 void Rotate(int x,int c)//旋转操作,c=0 表示左旋,c=1 表示右旋
109 {
110     int y=pre[x];
111     Push_Down(y);// 先将Y结点的标记向下传递(因为Y在上面)
112     Push_Down(x);//再把X的标记向下传递
113     ch[y][!c]=ch[x][c];//类似SBT,要把其中一个分支先给父节点
114     pre[ch[x][c]]=y;
115     pre[x]=pre[y];
116     if(pre[y])//如果父节点不是根结点,则要和父节点的父节点连接起来
117     {
118         ch[pre[x]][ch[pre[y]][1]==y]=x;
119     }
120     pre[y]=x;
121     ch[x][c]=y;
122     Push_Up(y);
123 }
124 
125 void Splay(int x,int f)//Splay操作,把根结点x转到结点f的下面
126 {
127     Push_Down(x);
128     while(pre[x]!=f)
129     {
130         int y=pre[x];
131         if(pre[y]==f)//父结点的父亲即为f,执行单旋转
132             Rotate(x,ch[y][0]==x);
133         else
134         {
135             int z=pre[y];
136             int g=(ch[z][0]==y);
137             if(ch[y][g]==x)
138                 Rotate(x,!g),Rotate(x,g);//之字形旋转
139             else Rotate(y,g),Rotate(x,g);//一字形旋转
140         }
141     }
142     Push_Up(x);// 最后再维护X结点
143     if(f==0)//更新根结点
144     {
145         root=x;
146     }
147 }
148 
149 void Rotate_Under(int k,int f)//把第k位的数伸展到f下方
150 {
151     //找到处在中序遍历第k个结点,并将其旋转到结点f 的下面
152     int p=root;//从根结点开始
153     Push_Down(p);// 由于要访问p的子结点,将标记下传
154     while(size[ch[p][0]]!=k)//p的左子树的大小
155     {
156         if(k<size[ch[p][0]])// 第k个结点在p左边,向左走
157         {
158             p=ch[p][0];
159         }
160         else//否则在右边,而且在右子树中,这个结点不再是第k个
161         {
162             k-=(size[ch[p][0]]+1);
163             p=ch[p][1];
164         }
165         Push_Down(p);
166     }
167     Splay(p,f);//执行旋转
168 }
169 
170 int Insert(int k)//插入结点
171 {
172     int r=root;
173     while(ch[r][key[r]<k])
174         r=ch[r][key[r]<k];
175     New_Node(ch[r][k>key[r]],r,k);
176     //将新插入的结点更新至根结点
177     //Push_Up(r);
178     Splay(ch[r][k>key[r]],0);
179     return 1;
180 }
181 
182 int Get_Pre(int x)//找前驱,即左子树的最右结点
183 {
184     int tmp=ch[x][0];
185     if(tmp==0)
186     return INF;
187     while(ch[tmp][1])
188     {
189         tmp=ch[tmp][1];
190     }
191     return key[x]-key[tmp];
192 }
193 
194 int Get_Next(int x)//找后继,即右子树的最左结点
195 {
196     int tmp=ch[x][1];
197     if(tmp==0)
198     return INF;
199     while(ch[tmp][0])
200     {
201         tmp=ch[tmp][0];
202     }
203     return key[tmp]-key[x];
204 }
205 
206 LL Query(int l,int r)//查询[l,r]之间的和
207 {
208     Rotate_Under(l-1,0);
209     Rotate_Under(r+1,root);
210     return sum[Key_value];
211 }
212 
213 void Update(int l,int r)//更新
214 {
215     int k;
216     scanf("%d",&k);
217     Rotate_Under(l-1,0);
218     Rotate_Under(r+1,root);
219     add[Key_value]+=k;
220     sum[Key_value]+=size[Key_value]*k;
221 }
222 
223 void Init()//初始化
224 {
225     for(int i=0; i<n; i++)
226         scanf("%d",&a[i]);
227     ch[0][0]=ch[0][1]=pre[0]=size[0]=0;
228     add[0]=sum[0]=0;
229     root=tot=0;
230     New_Node(root,0,-1);
231     New_Node(ch[root][1],root,-1);   //头尾各加入一个空位
232     size[root]=2;
233     Build_Tree(Key_value,0,n-1,ch[root][1]);  //让所有数据夹在两个-1之间
234     Push_Up(ch[root][1]);
235     Push_Up(root);
236 }
237 
238 int main()
239 {
240     while(~scanf("%d%d",&n,&q))
241     {
242         Init();
243         while(q--)
244         {
245             char op;
246             scanf(" %c",&op);
247             int x,y;
248             scanf("%d%d",&x,&y);
249             if(op==Q)
250                 printf("%lld\n",Query(x,y));
251             else
252                 Update(x,y);
253         }
254     }
255     return 0;
256 }

 

Splay Tree 模板

标签:

原文地址:http://www.cnblogs.com/wyj-jenny/p/5778655.html

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