写在前面
首先你需要了解 二叉搜索树(百度链接)。
这就是一个标准的二叉搜索树,是一颗满二叉树
很好,树高 l o g n log n logn ,查询的复杂度也就是 l o g n log n logn。
但是总有 毒瘤 的出题人卡数据,把你的树卡成链
查询复杂度为 n n n,这肯定不行啊,时间复杂度跟暴力一样了,怎么才能把树造的平衡一点呢。
正文
Treap 的由来是 Tree + Heap 既是一棵树,又有堆的性质,所以我们有时候叫他树堆 。
目的
因为一组数据 可以造很多种各不相同的二叉搜索树,所以 一条链的树 可以通过 变换形态 在 本质不变(中序遍历不变)的情况下变成一棵 平衡二叉树。
这时候可以加入一个 随机优先级(本文用 rd 表示)(一般用随机数),用堆的形式来 维护。
如何操作呢???
操作
如何维护优先级呢???
旋转
旋转
旋转分两种,右旋 和 左旋
右旋
当优先级不满足时,如下图
(规定优先级大的放上面)
如图可知,D的根节点 < B < E的根节点 < A < C 的根节点 (二叉搜索树的性质,此处比的是节点的值的大小,不是优先级!)
但是 B的优先级 比 A的优先级 大,怎么才能让B旋上去呢???
根据二叉搜索树的性质,右旋后的树 应该满足
D的根节点 < B < E的根节点 < A < C 的根节点,
所以应该长这样
右旋后 :
满足了
D的根节点 < B < E的根节点 < A < C 的根节点,
可以观察发现
A的左儿子 变成了 树根,
B的右儿子 变成了 A,
A的左子树 变成了 E树,
B树 的节点数 变成了 A树 的节点数
A树 的节点数 变成了 E树节点数 + C树节点数
代码实现 :
void rturn (int &now) {
//右旋,左节点上移
int p = t[now].l;
t[now].l = t[p].r;//A的左子树变成E树
t[p].r = now;//树根(B)的右儿子变成A
t[p].sz = t[now].sz;//B树 的节点数 变成了 A树 的节点数
update (now);//A树 的节点数 变成了 E树节点数 + C树节点数
now = p;//B成为树根
return ;
}
演示一遍