平衡树
平衡树
splay
伸展树特点就是每次操作完一个节点,将其旋转至根,保证BST性质的同时保持平衡不至于退化成链。对于一条长链,底端的节点旋转至顶会让这条链被压缩。
旋转rotate操作,分为左旋与右旋
伸展splay操作是旋转的组合,将某子树内节点x变为根节点。
- 当x已经成为根的子节点时,做单次旋转
- 否则存在x的父节点y,爷节点z
- x-y-z处于一条“直链”,则先旋转y再旋转x
- x-y-z处于一条“折链”,则先旋转x再旋转x
P3369 【模板】普通平衡树
您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作:
- 插入 $x$ 数
- 删除 $x$ 数(若有多个相同的数,应只删除一个)
- 查询 $x$ 数的排名(排名定义为比当前数小的数的个数 $+1$ )
- 查询排名为 $x$ 的数
- 求 $x$ 的前驱(前驱定义为小于 $x$,且最大的数)
- 求 $x$ 的后继(后继定义为大于 $x$,且最小的数)
基于伸展操作,可以实现以下操作
- 插入x。二叉查找树的性质可以找到x,让节点计数值自增;否则若x不存在,则会增加一个叶子x。然后为了保证不退化成链,让x旋转至根。
- 删除x。让x前驱旋转至根,再让x后继旋转至根的右儿子,此时x必是叶子。减少x节点计数值,若为0则直接删除节点x。
- 找到x的排名。x旋转至根,则左子树的大小+1就是x的排名。(注意具体实现时,会添加正负无穷哨兵避免边界特判的复杂实现)
- 找到排名为r的值。基于各子树大小与二叉查找树的性质实现。
- 找到x的前驱/后继。让x旋转至根,找左/右子树中的最大/小值即为前驱/后继。
所有操作均摊$O(logn)$
1 | // https://www.luogu.com.cn/problem/P3369 |
P3391 【模板】文艺平衡树
您需要写一种数据结构,来维护一个有序数列。
其中需要提供以下操作:翻转一个区间,例如原有序序列是 $5\ 4\ 3\ 2\ 1$,翻转区间是 $[2,4]$ 的话,结果是 $5\ 2\ 3\ 4\ 1$。
第一行两个正整数 $n,m$,表示序列长度与操作个数。序列中第 $i$ 项初始为 $i$。
接下来 $m$ 行,每行两个正整数 $l,r$,表示翻转的区间。
输出一行 $n$ 个正整数,表示原始序列经过 $m$ 次变换后的结果。
对于已经构成的二叉树,其中序遍历是原有序列,我们对区间的反转实际是对其中一颗子树的反转?实际并不是。与线段树类似一个区间可能覆盖了多颗子树。
但是我们可以利用splay操作将区间[l,r] 内的节点聚集在一颗子树上。具体做法就是找到排名为l-1的节点编号x,找到排名为r+1的节点编号y。通过伸展操作让x为根,y为x的右儿子。这时区间[l,r] 所有节点都在y的左子树上。
我们要反转区间[l,r] 。只需对这颗子树的反转,可以为其打上懒标记。懒标记的作用在于当遍历到含懒标记的节点时交换左右儿子,并传递懒标记。对于打上偶数次懒标记相当于交换偶数次,也就是无需交换。
我们发现本题构造了一颗二叉平衡树,但是却不是二叉搜索树(反转操作后,中序遍历二叉树并不是有序的)。但是找到排名为k的节点编号却可以用二叉搜索实现,其二叉搜索的条件取决于子树的节点数目而不是子节点的值。
我们找到排名为k的节点编号x,然后将x旋转至根,这个过程中时不会遇到懒标记的,因为在找x的过程中所经过的节点都会处理懒标记。所以x旋转至根的这条链上不会遇到懒标记。
最后中序遍历输出,过程中遇到懒标记便处理并下传。
1 | // https://www.luogu.com.cn/problem/P3391 |
FHQ Treap
无旋Treap每颗子树内,节点权值val满足二叉查找树的性质,节点随机值key满足堆的性质。节点的随机值保证了形成的树不会退化成链较为平衡。
通过分割操作将一颗树分为两颗treap树x和y,x树内节点值均小于等于v,y树内节点值均大于v。
通过合并操作将两颗treap树合为一颗treap树。
P3369 【模板】普通平衡树
您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作:
- 插入 $x$ 数
- 删除 $x$ 数(若有多个相同的数,应只删除一个)
- 查询 $x$ 数的排名(排名定义为比当前数小的数的个数 $+1$ )
- 查询排名为 $x$ 的数
- 求 $x$ 的前驱(前驱定义为小于 $x$,且最大的数)
- 求 $x$ 的后继(后继定义为大于 $x$,且最小的数)
通过分割与合并可以实现:
- 插入x。直接以x作为分割点分割成两树,然后将x视为一颗树,将三颗树合并即可。
- 删除x。先分割成三颗树a,b,c,a树内节点值均小于x,b树内节点值等于x,c树内节点均大于x。随后让b树两子节点合并成为新的b树。这样相当于删除了b树的根节点,然后让a,b,c三树合并。
- 找到x的排名。分割出节点值均小于x的树a,a的大小+1就是当前x的排名。
- 找到排名为r的值。利用子树大小与BST的性质,二叉查找。
- 找到x的前驱/后继。分割出节点值小/大于x的树a,a中最大/小值为x的前驱/后继
1 | // https://www.luogu.com.cn/problem/P3369 |
P3391 【模板】文艺平衡树
您需要写一种数据结构,来维护一个有序数列。
其中需要提供以下操作:翻转一个区间,例如原有序序列是 $5\ 4\ 3\ 2\ 1$,翻转区间是 $[2,4]$ 的话,结果是 $5\ 2\ 3\ 4\ 1$。
第一行两个正整数 $n,m$,表示序列长度与操作个数。序列中第 $i$ 项初始为 $i$。
接下来 $m$ 行,每行两个正整数 $l,r$,表示翻转的区间。
输出一行 $n$ 个正整数,表示原始序列经过 $m$ 次变换后的结果。
对于splay可以将利用伸展操作将区间[l,r] 内的节点聚集在一颗子树上。而无旋treap也可以将区间聚集到一颗树上,然后打上懒标记。可以按照排名分裂,先将区间[1,n]分为[1, r], [r+1, n],然后将[1, r]分为[1, l-1], [l, r] 。然后将[l,r]子树打上懒标记。
合并和分割操作遇到的每个节点都需要处理懒标记。
1 |
|