Spaly是基于二叉查找树实现的,数据结构
什么是二叉查找树呢?就是一棵树呗:joy: ,可是这棵树知足性质—一个节点的左孩子必定比它小,右孩子必定比它大ide
好比说函数
这就是一棵最基本二叉查找树spa
对于每次插入,它的指望复杂度大约是$logn$级别的,可是存在极端状况,好比9999999 9999998 9999997.....1这种数据,会直接被卡成$n^2$code
在这种状况下,平衡树出现了!blog
Splay是平衡树的一种,中文名为伸展树,由丹尼尔·斯立特Daniel Sleator和罗伯特·恩卓·塔扬Robert Endre Tarjan在1985年发明的(mmp怎么又是tarjan)排序
它的主要思想是:对于查找频率较高的节点,使其处于离根节点相对较近的节点。io
这样就能够保证了查找的效率class
那么如今问题来了:效率
这个玩意儿确实很差统计,可是你能够认为每次被查找的点查找频率相对较高,说白了就是你把每次查找到的点搬到根节点去
固然你也能够每次查找以后随机一个点做为根,因而Treaplay这种数据结构就诞生啦
这也是Splay这种数据结构所要实现的功能,接下来咱们详细的介绍一下
首先考虑一下,咱们要把一个点挪到根,那咱们首先要知道怎么让一个点挪到它的父节点
当X是Y的左孩子
这时候若是咱们让X成为Y的父亲,只会影响到3个点的关系
B与X,X与Y,X与R
根据二叉排序树的性质
B会成为Y的左儿子
Y会成为X的右儿子
X会成为R的儿子,具体是什么儿子,这个要看Y是R的啥儿子
通过变换以后,大概是这样
当X是Y的右孩子
本质上和上面是同样的,
变换后为
这两种代码单独实现都比较简单,我就不写了(其实是我懒)
可是这两种旋转状况很相似,第二种状况实际就是把第一种状况的X,Y换了换位置
咱们考虑一下能不能将这两种状况合并起来实现呢?
答案是确定的
首先咱们要获取到每个节点它是它爸爸的哪一个孩子,能够这么写
bool ident(int x) { return tree[tree[x].fa].ch[0] == x ? 0 : 1; }
若是是左孩子的话会返回0,右孩子会返回1
那么咱们不可贵到R,Y,X这三个节点的信息
int Y = tree[x].fa; int R = tree[Y].fa; int Yson = ident(x); //x是y的哪一个孩子 int Rson = ident(Y);
B的状况咱们能够根据X的状况推算出来,根据^运算的性质,0^1=1,1^1=0,2^1=3,3^1=2,并且B相对于X的位置必定是与X相对于Y的位置是相反的
(不然在旋转的过程当中不会对B产生影响)
int B = tree[x].ch[Yson ^ 1];
而后咱们考虑链接的过程
根据上面的图,不可贵到
B成为Y的哪一个儿子与X是Y的哪一个儿子是同样的
Y成为X的哪一个儿子与X是Y的哪一个儿子相反
X成为R的哪一个儿子与Y是R的哪一个儿子相同
connect(B, Y, Yson); connect(Y, x, Yson ^ 1); connect(x, R, Rson);
connect函数这么写,挺显然的
void connect(int x, int fa, int how) { //x节点将成为fa节点的how孩子 tree[x].fa = fa; tree[fa].ch[how] = x; }
单旋函数就是这样了,利用这个函数就能够实现把一个节点搬到它的爸爸那儿了,
Splay(x,to)是实现把x节点搬到to节点
最简单的办法,对于x这个节点,每次上旋直到to
可是!
若是你真的这么写,可能会T成SB,出题人可能会构造数据把单旋卡成$n^2$,不要问我为何!(实际上是我不知道)
一个感性的理解是这样的
把一个点双旋到根,可使得从根到它的路径上的全部点的深度变为大约原来的一半,其它点的深度最多增长2
或者你能够了解一下为啥单旋是错的
下面咱们介绍一下双旋的Splay
这里的状况有不少,可是总的来讲就三种状况
1.to是x的爸爸,
这样的话吧x旋转上去就好
update in 2018.2.19
这里可能写错了一个地方(其实也没有写错)
由于咱们在双旋的时候会改变三个点的关系,为了方别写,因此咱们开始的时候把to设置为to的爸爸
if (tree[tree[x].fa].fa == to) rotate(x);
2.x和他爸爸和他爸爸的爸爸在一条线上
这时候先把Y旋转上去,再把X旋转上去就好
else if (ident(x) == ident(tree[x].fa)) rotate(tree[x].fa), rotate(x);
3.x和他爸爸和他爸爸的爸爸不在一条线上
这时候把X旋转两次就好
总的代码:
void splay(int x, int to) { to = tree[to].fa; while (tree[x].fa != to) { if (tree[tree[x].fa].fa == to) rotate(x); else if (ident(x) == ident(tree[x].fa)) rotate(tree[x].fa), rotate(x); else rotate(x), rotate(x); } }
至此,Spaly的最核心最基本的操做已经讲解完毕
至于这玩意儿怎么用,以及能实现什么功能,且听下回分解