介绍
无旋treap ,又称为 非旋treap , fhq_treap ,是一个非常实用的数据结构,用来优化一般的 二叉搜索树 所解决不了的问题,大大提高了时间的效率,在解决平衡树问题的应用较广。
其中用处相同的还有 splay ,但 splay 代码码量大,本蒟蒻一年前开始学,现在连无旋treap都会了,还不会splay,但是splay是 LCT 的重要前置知识,所以两种数据结构都有学的必要性。
背景
一般的二叉搜索树 (指某节点的左子树权值均比节点权值大(或小),右子树权值均比节点小(或大)的一颗二叉树) 的主要作用是查询第 k k k 大的权值或权值 x x x 的排名,一般的时间复杂度为 O ( n l o g n ) O(n log n) O(nlogn) ,但是数据可以将它卡成一条链,此时的时间复杂度为 O ( n 2 ) O(n^2) O(n2)
可以看出,当这一颗二叉搜索树的形状越扁(即 深度越小 )时,其时间复杂度就会更优,于是平衡树应运而生。
原理
无旋treap,肯定和常规数据结构(指splay)不一样,它是不用旋转的(少了毒瘤操作真开心QwQ)。
treap=tree+heap(即“树”+“堆”) 所以treap既有堆的性质,又有二叉搜索树的性质,而其中堆的性质,就是我们把树拍扁的关键。
无旋treap的核心操作有两个,分别为 分裂(split) 和 合并(merge) ,分裂就是将一棵树掰开两瓣,合并就是将两棵树合为一颗树。
声明
struct node{
int ls;//左儿子
int rs;//右儿子
int sz;//子树大小
int val;//节点权值
int id;//编号(维护堆的性质)
}tree[2000200];
Pushup
不必赘述,将左右子树的 s i z e size size 加起来即可
void pushup(int x)
{
tree[x].sz=tree[ls(x)].sz+tree[rs(x)].sz+1;
}
Split
假设将treap掰成根为 a a a 和根为 b b b 的两颗treap,其中 a a a 子树权值小于等于(大于等于) v a l val val , b b b 子树权值大于(小于) v a l val val ,假设原treap根为 r t rt rt 。
若 t r e e [ r t ] . v a l ≤ v a l tree[rt].val \le val tree[rt].val≤val 则证明 r t rt rt 的左子树所有的点都可以放在 a a a