Splay
IAKPSC就是个乐子,学的时候不写学习笔记,现在全忘了,不像人类
Splay 是一种平衡树,通过旋转维护平衡可以做到树高均摊 log
"Splay" ,查一下词典,是伸展的意思
顾名思义," 伸展 " 操作是维护一颗 Splay 的关键,而伸展的核心,便是旋转
盗一张图
旋转就是在满足二叉搜索树性质的同时改变树的形态
那上图是如何旋转的呢
再盗用先人一句口诀:左旋拎右左挂右,右旋拎左右挂左
这是hyw呢?我以右旋为例,略微解释一下
拎出 1 的左儿子 2,将 1 的父亲变为 2 ,2 的父亲变为原先 1 的父亲 ,2 原来的右儿子变为 1 的左儿子
旋转的 code
const int N=2e5+5;
struct node{
int s[2];
int siz,fa,key;
node(){
s[0]=s[1]=siz=fa=key=0;
}
}tree[N];
#define fa(k) tree[k].fa
#define ls(k) tree[k].s[0]
#define rs(k) tree[k].s[1]
void maintain(int k){tree[k].siz=tree[ls(k)].siz+tree[rs(k)].siz+1;}
bool get(int k){return k==rs(fa(k));}
void rotate(int k)
{
int x=fa(k),y=fa(x);bool c=get(k);
if(tree[k].s[c^1]) fa(tree[k].s[c^1])=x;
tree[x].s[c]=tree[k].s[c^1];
tree[k].s[c^1]=x;
fa(x)=k;fa(k)=y;
if(y) tree[y].s[x==tree[y].s[1]]=k;
maintain(x);maintain(k);
}
然后就是伸展了,伸展操作本质就是把一个点旋转至另一个点的下方
void splay(int x,int y)
{
for(int f=fa(x);f=fa(x),f!=y;rotate(x))
{
if(fa(f)!=y) rotate(get(x)==get(f)?f:x);
}
if(!y) root=x;
}
这是压行写法,描述了一个双旋的过程,若 x 的爷爷不是目标点,旋转 x 的父亲 f ,再旋转 x ,通过双旋改变树的形态,至于这个三目运算符,貌似指的是如果 fa 在 x 同侧就旋转 x ,感性理解一下,可以提升效率
至于剩下的,就是依据平衡树特性以及 splay 操作的 “瞎搞搞” 了,读者发明不难

浙公网安备 33010602011771号