Splay模板

本文详细介绍了伸展树(SplayTree),一种自调整形式的二叉查找树,能够高效地完成插入、查找和删除操作。文章深入探讨了伸展树的工作原理,包括其旋转操作、重构机制以及如何维护树的平衡,同时提供了实现代码示例。

伸展树(Splay Tree)是一种二叉排序树,它能在O(log n)内完成插入、查找和删除操作。它由Daniel SleatorRobert Tarjan创造。

它的优势在于不需要记录用于平衡树的冗余信息。

在伸展树上的一般操作都基于伸展操作。

假设想要对一个二叉查找树执行一系列的查找操作。为了使整个查找时间更小,被查频率高的那些条目就应当经常处于靠近树根的位置。于是想到设计一个简单方法, 在每次查找之后对树进行重构,把被查找的条目搬移到离树根近一些的地方。splay tree应运而生。splay tree是一种自调整形式的二叉查找树,它会沿着从某个节点到树根之间的路径,通过一系列的旋转把这个节点搬移到树根去。/*载自http://zh.wikipedia.org/wiki/%E4%BC%B8%E5%B1%95%E6%A0%91*/

 

补充:实际上是对序列序号维护者二叉搜索树BST,即对于序号左子树<根<右子树。如此根据子树大小可定位到第k个数。进一步,进行zig(右旋)、zag(左旋)是,考虑了顺序问题,可以尽量保持树的平衡。具体可以看杨思雨的论文证明。

 

http://www.notonlysuccess.com/index.php/splay-tree/

 

【功能】

维护数列,实现区间插入、删除、修改、翻转等操作!

均摊复杂度log(N)

【主要函数】

Rotate(int x, int f);//f=0:zag, f=1:zig

Splay(int x,int goal)//把x结点移动到goal的下面

RotateTo(int k,int goal)//把第k个数移动到goal的下面。

以上三个函数不变!

push_down(int x);

push_up(int x);

NewNode(int &x,int c);

MakeTree(int &x, int l, int r, int f);

init(int n); 初始化,一般建立两个结点作为边界。

其他根据题目需要设定。

  1 #include <cstdio>
  2 #define keyTree (ch[ ch[root][1] ][0])
  3 
  4 const int maxn = 222222;
  5 struct SplayTree{
  6     int sz[maxn];
  7     int ch[maxn][2];
  8     int pre[maxn];
  9     int root , top1;
 10 
 11     void Rotate(int x,int f) {//f=0:zag, f=1:zig
 12         int y = pre[x];
 13         push_down(y);
 14         push_down(x);
 15         ch[y][!f] = ch[x][f];
 16         pre[ ch[x][f] ] = y;
 17         pre[x] = pre[y];
 18         if(pre[x]) ch[ pre[y] ][ ch[pre[y]][1] == y ] = x;
 19         ch[x][f] = y;
 20         pre[y] = x;
 21         push_up(y);
 22     }
 23     void Splay(int x,int goal) {
 24         push_down(x);
 25         while(pre[x] != goal) {
 26             if(pre[pre[x]] == goal) {
 27                 Rotate(x , ch[pre[x]][0] == x);
 28             } else {
 29                 int y = pre[x] , z = pre[y];
 30                 int f = (ch[z][0] == y);
 31                 if(ch[y][f] == x) {
 32                     Rotate(x , !f) , Rotate(x , f);//之字型
 33                 } else {
 34                     Rotate(y , f) , Rotate(x , f);//一字型
 35                 }
 36             }
 37         }
 38         push_up(x);
 39         if(goal == 0) root = x;
 40     }
 41     void RotateTo(int k,int goal) {//把第k位的数转到goal下边
 42         int x = root;
 43         push_down(x);
 44         while(sz[ ch[x][0] ] != k) {
 45             if(k < sz[ ch[x][0] ]) {
 46                 x = ch[x][0];
 47             } else {
 48                 k -= (sz[ ch[x][0] ] + 1);
 49                 x = ch[x][1];
 50             }
 51             push_down(x);
 52         }
 53         Splay(x,goal);
 54     }
 55     //以上一般不修改//////////////////////////////////////////////////////////////////////////////
 56     void debug() {printf("%d\n",root);Treaval(root);}
 57     void Treaval(int x) {
 58         if(x) {
 59             Treaval(ch[x][0]);
 60             printf("结点%2d:左儿子 %2d 右儿子 %2d 父结点 %2d size = %2d ,val = %2d\n",x,ch[x][0],ch[x][1],pre[x],sz[x],val[x]);
 61             Treaval(ch[x][1]);
 62         }
 63     }
 64     //以上Debug
 65 
 66 
 67     //以下是题目的特定函数:
 68     void NewNode(int &x,int c) {
 69         x = ++top1;
 70         ch[x][0] = ch[x][1] = pre[x] = 0;
 71         sz[x] = 1;
 72 
 73         val[x] = sum[x] = c;/*这是题目特定函数*/
 74         add[x] = 0;
 75     }
 76 
 77     //把延迟标记推到孩子
 78     void push_down(int x) {/*这是题目特定函数*/
 79     }
 80     //把孩子状态更新上来
 81     void push_up(int x) {
 82         sz[x] = 1 + sz[ ch[x][0] ] + sz[ ch[x][1] ];
 83         /*以下这是题目特定函数*/
 84         sum[x] = add[x] + val[x] + sum[ ch[x][0] ] + sum[ ch[x][1] ];
 85     }
 86 
 87     /*初始化*/
 88     void makeTree(int &x,int l,int r,int f) {
 89         if(l > r) return ;
 90         int m = (l + r)>>1;
 91         NewNode(x , num[m]);        /*num[m]权值改成题目所需的*/
 92         makeTree(ch[x][0] , l , m - 1 , x);
 93         makeTree(ch[x][1] , m + 1 , r , x);
 94         pre[x] = f;
 95         push_up(x);
 96     }
 97     void init(int n) {/*这是题目特定函数*/
 98         ch[0][0] = ch[0][1] = pre[0] = sz[0] = 0;
 99         add[0] = sum[0] = 0;
100 
101         root = top1 = 0;
102         //为了方便处理边界,加两个边界顶点
103         NewNode(root , -1);
104         NewNode(ch[root][1] , -1);
105         pre[top1] = root;
106         sz[root] = 2;
107 
108         /***这里完成num[]数组赋值***/
109 
110         makeTree(keyTree , 0 , n-1 , ch[root][1]);
111         push_up(ch[root][1]);
112         push_up(root);
113     }
114     /*更新*/
115     void update( ) {/*这是题目特定函数*/
116     }
117     /*询问*/
118     void query() {/*这是题目特定函数*/
119     }
120     /* 删除[u,v]区间*/
121     int spit(int u,int v){
122         RotateTo(u - 1 , 0 );
123         RotateTo(v + 1 , root );
124         int res = keyTree;
125         keyTree = 0 ;
126         push_up(ch[root][1]);
127         push_up(root);
128         return res ;
129     }
130     /*lazy标记*/
131     void lazy(int u,int v )
132     {
133         RotateTo(u - 1 , 0 );
134         RotateTo(v + 1 , root );
135         /*以下根据题目需要完成*/
136         //rev(keyTree);
137         //Splay(keyTree,root);
138     }
139     /*合并操作,把ts代表的子树合并到k下面*/
140     void merge(int k,int ts)
141     {
142         RotateTo(k - 1 ,0 );
143         RotateTo(k , root);
144         keyTree = ts ;
145         pre[ts] = ch[root][1];
146         push_up(ch[root][1]);
147         push_up(root);
148     }
149     /*插入数或者区间在k前面,根据题目需要完成,以下是点*/
150     void insert(int k,int val)
151     {
152         cnt ++ ;
153         RotateTo(k - 1 , 0 );
154         RotateTo(k , root ) ;
155         NewNode(keyTree,val);
156         pre[keyTree] = ch[root][1];
157         push_up(ch[root][1]);
158         push_up(root);
159     }
160     /*得到第k个数*/
161     int getkth(int k )
162     {
163         int x = root ;
164         push_down(x);
165         while(sz[ ch[x][0] ] != k)
166         {
167             // printf("x = %d k = %d sz[x] = %d\n",x,k,sz[x]);
168             if(k < sz[ ch[x][0] ])
169             {
170                 x = ch[x][0];
171             }
172             else
173             {
174                 k -= (sz[ ch[x][0] ] + 1);
175                 x = ch[x][1];
176             }
177             push_down(x);
178         }
179         return val[x];
180     }
181     /*删除k这个数*/
182     void del(int k)
183     {
184         RotateTo(k - 1 , 0);
185         RotateTo(k + 1 , root);
186         //printf("val = %d\n",val[keyTree]);
187         //printf("sz = %d\n",sz[keyTree]);
188         cnt -- ;
189         keyTree = 0 ;
190         push_up(ch[root][1]);
191         push_up(root);
192         // erase(keyTree);
193     }
194     /*这是题目特定变量*/
195     int cnt;
196     int num[maxn];
197     int val[maxn];
198     int add[maxn];
199     long long sum[maxn];
200 }spt;
201 
202 
203 int main() {
204 
205     //初始化
206     int n;
207     spt.init(n);
208 
209     return 0;
210 }
Splay模板

 

转载于:https://www.cnblogs.com/wangsouc/articles/3649121.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值