学习Splay的时候参考了很多不同的资料,然而参考资料太杂的后果就是模板调出来一直都有问题,尤其是最后发现网上找的各种资料均有不同程度的错误。
好在啃了几天之后终于算是啃下来了。
Splay也算是平衡树的一种,但是跟AVL树、SBT不同的是,Splay并不是一直保持严格的平衡,因此在速度上可能要慢一些,但是统计学上仍能保证Splay具有O(logn)的均摊复杂度。
Splay原生不需要附加任何空间,它的先天优势是其特有的Splay操作可以非常灵活地改变整棵树的结构形态,完成一般线段树、平衡树做不到的任务。
- 基本操作-左右旋
Splay的基本操作与其他平衡树相似,都是进行结点的左右旋转。与之前写的SBT相比,Splay除了左右儿子域以外还需要一个father域,这个是因为下面的Splay操作需要用到父节点来判断当前的形态。
- 核心操作-splay
Splay(x,y)的作用是将当前点x旋转到y的子结点上,一般写y=0时则将x旋转到整棵树的根。这个操作在splay的所有函数中基本上最后都要加上,简单的理解就是将常用的结点尽可能地上移到深度较浅的地方,据统计若去掉函数最后对操作结点的splay,则整个程序将会慢上数倍。
splay基本的想法是根据当前结点父节点形态的不同,分成四种情况,逐步将结点旋转到根上。
- 进阶操作
插入、查找值、查找第k个等等...基本与普通的二叉查找树相似,但是记得在最后要加上splay,将使用过的结点旋转到根。
特殊的删除操作:
splay本身是一棵二叉搜索树,可以进行一般的删除操作。另外,二叉搜索树的性质配合splay的操作可以做到O(logn)动态删除一整个区间的结点,而一般的平衡树则只能一个一个删除。
例如:删除大于l且小于r的数,先将序号小于l的最大结点伸展到根,序号大于r的最小的结点伸展到根的右子树,则此时,r的后继结点的左子树中就存放着从l到r的所有结点,此时只要把这棵子树整个断开即可。
............
/* *********************************************** MYID : Chen Fan LANG : G++ PROG : Splay Tree ************************************************ */ #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; #define MAXN 100000 int sons[MAXN][2]; int father[MAXN],size[MAXN],data[MAXN]; int spt=0,spttail=0; void rotate(int x,int w) //rotate(node,0/1) { int y=father[x]; sons[y][1-w]=sons[x][w]; if (sons[x][w]) father[sons[x][w]]=y; father[x]=father[y]; if (father[y]) if (y==sons[father[y]][0]) sons[father[y]][0]=x; else sons[father[y]][1]=x; sons[x][w]=y; father[y]=x; size[x]=size[y]; size[y]=size[sons[y][0]]+size[sons[y][1]]+1; } void splay(int x,int y) //splay(node,position) { if (!x) return ; while(father[x]!=y) { if (father[father[x]]==y) if (x==sons[father[x]][0]) rotate(x,1); else rotate(x,0); else if (father[x]==sons[father[father[x]]][0]) if (x==sons[father[x]][0]) { rotate(father[x],1); rotate(x,1); } else { rotate(x,0); rotate(x,1); } else if (x==sons[father[x]][1]) { rotate(father[x],0); rotate(x,0); } else { rotate(x,1); rotate(x,0); } } if (!y) spt=x; } void search(int x,int w) { while(data[x]!=w) { if (w<data[x]) { if (sons[x][0]) x=sons[x][0]; else break; } else if (w>data[x]) { if (sons[x][1]) x=sons[x][1]; else break; } } splay(x,0); } void insert(int w) //insert(value) { spttail++; data[spttail]=w; size[spttail]=1; sons[spttail][0]=0; sons[spttail][1]=0; if (!spt) { father[spttail]=0; spt=spttail; } else { int x=spt; while(1) { size[x]++; if (w<data[x]) if (sons[x][0]) x=sons[x][0]; else break; else if (sons[x][1]) x=sons[x][1]; else break; } father[spttail]=x; if (w<data[x]) sons[x][0]=spttail; else sons[x][1]=spttail; splay(spttail,0); } } void select(int x,int v) //select(root,k) { while(v!=size[sons[x][0]]+1) { if (v<=size[sons[x][0]]) x=sons[x][0]; else { v-=size[sons[x][0]]+1; x=sons[x][1]; } } splay(x,0); } int succ(int t) { t=sons[t][1]; while(sons[t][0]) t=sons[t][0]; splay(t,0); return t; } void del(int x) //del(number) { splay(x,0); int y=sons[x][0]; while(!sons[y][1]) { y=sons[y][1]; } int z=sons[x][1]; while(!sons[z][0]) { z=sons[z][0]; } if (y+z==0) { spt=0; spttail=0; return ; } if (y) { splay(y,0); size[y]--; } if (z) { splay(z,y); sons[z][0]=0; size[z]--; } } int rank(int v) //rank(value) { search(spt,v); return size[sons[spt][0]]+1; } int main() { memset(father,0,sizeof(father)); memset(size,0,sizeof(size)); memset(sons,0,sizeof(sons)); memset(data,0,sizeof(data)); spt=0; spttail=0; return 0; }