FHQ Treap是什么?
FHQ Treap
,又名无旋Treap
,是一种不需要旋转的平衡树,是范浩强基于Treap
发明的。FHQ Treap
具有代码短,易理解,速度快的优点。(当然跟红黑树比一下就是……)至少它在OI
中算是很优秀的数据结构了。
前置知识:
C++
二叉搜索树
的基本性质,下面会讲二叉堆
二叉搜索树的基本性质
很简单,就这几个。
- 在
二叉搜索树
中,每个结点都满足左子树的结点的值都小于等于自己的值,右子树的结点的值都大于自己的值,左右子树也是二叉搜索树。 - 中序遍历
二叉搜索树
可以得到一个由这棵树的所有结点的值组成的有序序列。(即所有的值排序后的结果)
原理&代码实现
本文中,
Treap
就是指有旋Treap
FHQ Treap
不是通过旋转来保持平衡的,而是通过两个函数split
和merge
。顾名思义,split
就是分裂,merge
就是合并。当然,从最底层的原理来看,还不是这两个函数。FHQ Treap
中的Treap
代表Tree + Heap
,也就是说,FHQ Treap
会按二叉搜索树
一样根据键值排序结点,并且随机赋给每个结点一个优先级,按照二叉堆的顺序排序结点(这里用大根堆)。Treap
通过旋转,使平衡树同时满足这两个性质,从而达到平衡。而FHQ Treap
通过调用merge
函数时使平衡树满足堆序,实现原理与Treap
不同。
结点信息
FHQ Treap
是一个二叉树,所以可以写出这样的代码:
template <typename T, int MaxSize>
class FHQTreap
{
public:
FHQTreap() { Seed = (int)(MaxSize * 565463ll % 2147483647); }
// ...
private:
struct Node
{
T Key;
int Left, Right, Size, Priority;
} Tree[MaxSize];
int Seed, Total, Root;
int random() { return Seed = (int)(Seed * 104831ll % 0x7fffffff); }
void pushup(int root) {
if(root != 0) {
Tree[root].Size = Tree[Tree[root].Left].Size + Tree[Tree[root].Right].Size + 1; // + 1是要算上自己
}
}
// ...
}
Node
即结点,里面的Key
就是要存的值,Priority
即优先级。Seed
就是随机数种子,在构造函数中初始化,random()
会生成一个在int
范围内的整数,作为结点的优先级。(我自己写随机数生成函数只是个人习惯)
构造新结点
int create(T key) {
int root = ++Total;
Tree[root].Key = key;
Tree[root].Size = 1;
Tree[root].Left = Tree[root].Right = 0;
Tree[root].Priority = rad();
return root;
}
create(T key)
会初始化一个结点,并返回它的ID
,大家也可以用指针实现。这里比较简单,就不多解释了。
split函数
split
分为两种:
- 按值分裂:根据一个值\(key\)把一棵树分裂成两棵树,一棵树的值全部小于等于\(key\),另外一棵全部大于\(key\)
- 按大小分裂:根据一个值\(size\)分裂树,一棵的大小为\(size\),另外一棵为剩下的。
按值分裂
如上图。这里split
函数简化了,只写了值。根据图可以看出,比\(25\)小的结点都被分裂到以\(x\)为根的树上&#x