伸展树(Splay Tree)是一种二叉排序树,它能在O(log n)内完成插入、查找和删除操作。它由Daniel Sleator和Robert 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 }
本文详细介绍了伸展树(SplayTree),一种自调整形式的二叉查找树,能够高效地完成插入、查找和删除操作。文章深入探讨了伸展树的工作原理,包括其旋转操作、重构机制以及如何维护树的平衡,同时提供了实现代码示例。
1万+

被折叠的 条评论
为什么被折叠?



