1.伸展树(splay)
1.1性质
伸展树是一颗二叉搜索树,可以在 O ( l o g n ) O(log n) O(logn) 的时间内完成加入,删除,查询操作。
一般的二叉搜索树会被卡成一条链,而伸展树是依靠在维护二叉搜索树的同时,使得这颗树的深度尽可能地小。
伸展树依靠旋转操作来维护,将刚访问过的节点旋转至树根,加速后续操作。
伸展树无需时刻保持全树平衡,维护中序遍历不变,各种操作的均摊时间复杂度为 ( l o g n ) (log n) (logn) 。
伸展树一般都能做到线段树的操作,在此基础上,伸展树还支持区间添加,区间删除,区间反转等操作。
伸展树为 L C T LCT LCT 的前置知识。
1.2操作
伸展操作是在保持伸展树有序的前提下,通过旋转来将元素 x x x 调整到 g o a l goal goal 的子节点,若 g o a l goal goal 为 0 0 0 ,则将 x x x 调整至树的根部 ,旋转包括右旋 ( z i g ) (zig) (zig) 和左旋 ( z a g ) (zag) (zag) 两种基本操作:
右旋 z i g ( x ) zig(x) zig(x):
(CCF的图)
将 x x x 旋转至 y y y 的父亲节点,将 x x x 的右子树 x r xr xr 接到 y y y 的左子树,将 y y y 接到 x x x 的右儿子。
左旋 z a g ( x ) zag(x) zag(x) :
(CCF的图)
将 x x x 旋转至 y y y 的父亲的儿子,将 x x x 的左子树 x l xl xl 接到 y y y的右子树,将 y y y 接到 x x x 的左儿子。
旋转代码
void rotate(int x)
{
int father=tree[x].fat;
int grandfather=tree[father].fat;
int gson=find_son(father,grandfather);
int fson=find_son(x,father);
int sn=tree[x].ch[fson^1];
change(sn,father,fson);
change(father,x,fson^1);
change(x,grandfather,gson);
pushup(father),pushup(x);
}
1.2.1伸展操作:
逐层伸展(单旋)
每次只旋一次,会被卡到 O ( n ) O(n) O(n)。
逐层伸展(双层)
分两步走,时间复杂度 O ( l o g n ) O(log n) O(logn) 。
情况1: 若点 y y y 为根节点,则只需进行一次操作( z i g ( x ) zig(x) zig(x) 或 z a g ( x ) zag(x) zag(x))
情况2:若点 y y y 与点 x x x 同为左儿子(右儿子),先右旋(左旋) y y y 再右旋 (左旋) x x x 。
情况3:若点 y y y 与点 x x x 不同为左儿子(右儿子),先右旋(左旋) x x x ,再左旋(右旋) x x x 。
伸展代码:
void splay(int x,int to)
{
to=tree[to].fat;
while(tree[x].fat!=to)
{
if(tree[tree[x].fat].fat==to)
rotate(x);
else if(find_son(tree[x].fat,tree[tree[x].fat].fat)==find_son(x,tree[x].fat))
rotate(tree[x].fat),rotate(x);
else
rotate(x),rotate(x);
}
}
1.2.2基本操作:
1.2.2.1查找操作:再伸展树中查找 v a l val val ,若查找成功,将 x x