(这里是Splay基础操作,reserve什么的会在下一篇里面讲)
好久之前就说要学Splay了,结果苟到现在才学习。
可能是最近良心发现自己实在太弱了,听数学又听不懂只好多学点不要脑子的数据结构。
感觉Splay比Treap良心多了——代码真的好写。
对于Splay显然可以维护Treap的所有操作,并且本质是BST。
先看看Splay是怎么维护普通平衡树操作的吧。
首先先定义一些基础的变量(若不作特殊说明这些变量的意义不变)
int t[N][2] // t[x][0]表示节点x的左子树,t[x][1]表示节点x的右子树
int cnt[N] // cnt[x]表示节点x存储多少个重复的数
int val[N] // val[x]表示节点x存储数的大小
int par[N] // par[x]表示节点x的直接父亲,特别的,根节点的直接父亲为0
int size[N] // size[x]表示在BST中x子树中存储数的个数
Check(x) 函数 :
询问节点x是其父亲的左儿子(return 0)还是右孩子(return 1)
int check (int x) { return rs(par[x])==x; }
由上述代码可知,根节点的check(root)值为0,即根节点是0节点的左儿子(Nothing Special)
Up(x)函数:
对于节点x维护其size值为两个孩子的size值+自身cnt值。
void up(int x){ size[x]=size[ls(x)]+size[rs(x)]+cnt[x]; }
Rotate(x)函数:
对于节点x旋转到其父亲节点,且不改变树BST性质。(使得树形态较为随机)
这里需要解释一下Rotate的解释和记忆方法(和Treap中Rotate类似)
对于一棵有根树(且父亲指向儿子的边有向),我们现在以把左儿子旋到父节点为例。
第1步,考虑4的右子树已经有元素了,考虑把右子树连接到父节点左儿子处。不改变BST性质。
在此基础上考虑第二步,就是吧2(4的直接父亲接到4的右边)不改变BST性质。
这个时候我们会发现,只进行第三部就可以完成一次rotate操作。
即连一条1指向4的边即可。
对于左边节点转到父亲节点,一般称之为右旋
对于右旋函数的代码,不难得到。
void rotate(int x){ int y=par[x]; t[y][0]=t[x][1]; par[t[x][1]]=y; t[x][1]=y; t[par[y]][check(y)]=x; par[x]=par[y]; par[y]=x; up(y); up(x); }