Treap 学习笔记
来源:互联网 发布:fm调频发射器软件 编辑:程序博客网 时间:2024/06/05 13:29
Treap
Treap,顾名思义就是 Tree + Heap。这么命名的原因就是它使用了二叉堆的性质来保持二叉树的平衡。
我们知道,一个二叉(小根)堆满足这样的性质:一个节点的两个儿子的值都小于节点本身的值。如果一个二叉查找树满足这样的性质,那么它就被称作 Treap。
Treap 中每个节点有 2 个值,其中一个满足二叉查找树的性质,一个满足大根堆的性质。把满足二叉查找树性质的值称作 w,把满足大根堆性质的值称作 prio。 对于 Treap 来说,当前节点的 w 值大于左儿子,小于右儿子。当前节点的 prio 值小于儿子节点的值。
每个节点的 w 我们无法改变,为了保证 Treap 的平衡性,我们需要让每个节点的 prio 都取一个随机值,这样我们就可以保证这棵树“基本平衡”。
随机数的生成
如果我们采用系统函数生成的随机数,会有出现重复的可能性。如果 prio 取到了重复的值,则必然会造成堆结构的混乱。
生成不重复的随机数的办法:
inline int random_f() { static int seed = 623; // seed 可以随便取? return seed = int(seed * 48271LL % 2147483647);}
48271 就可以取遍 1 - 2147483647 中的所有数字。
插入
给节点随机分配一个优先级,先把要插入的点插入到一个叶子上,然后跟维护堆一样,我们维护一个小根堆,如果当前节点的优先级比根大就旋转,如果当前节点是根的左儿子就右旋,如果当前节点是根的右儿子就左旋。
由于旋转是
其实也就是说,
右旋就是,让当前节点降为自己的右儿子,让左儿子代替自己,并让自己左儿子的右儿子成为自己的左儿子。
左旋相同,就是让当前节点降为自己的左儿子,让右儿子代替自己,并让自己右儿子的左儿子成为自己的右儿子。
删除
因为 Treap 满足堆性质,所以只需要把要删除的节点旋转到叶节点上,然后直接删除就可以了。
具体的方法:
如果该节点的左子节点的优先级小于右子节点的优先级,右旋该节点,使该节点降为右子树的根节点,然后访问右子树的根节点,继续操作;
反之,左旋该节点,使该节点降为左子树的根节点,然后访问左子树的根节点,继续操作,直到变成可以直接删除的节点。
(即:让优先级小的结点旋到上面,满足堆的性质)
删除最多进行
Luogu P3369 【模板】普通平衡树(Treap/SBT)
- 插入x数
- 删除x数(若有多个相同的数,因只删除一个)
- 查询x数的排名(排名定义为比当前数小的数的个数+1。若有多个相同的数,因输出最小的排名)
- 查询排名为x的数
- 求x的前驱(前驱定义为小于x,且最大的数)
- 求x的后继(后继定义为大于x,且最小的数)
#include <bits/stdc++.h>using namespace std;const int N = 2e5 + 5; inline int random_f() { static int seed = 623; return seed = int(seed * 48271LL % 2147483647);}struct Treap { int rt, cnt; int w[N], prio[N], size[N], lson[N], rson[N]; inline void clear() { rt = 0; cnt = 0; memset(w, 0, sizeof(w)); memset(prio, 0, sizeof(prio)); memset(size, 0, sizeof(size)); memset(lson, 0, sizeof(lson)); memset(rson, 0, sizeof(rson)); } inline void pushup(int &p) {size[p] = size[lson[p]] + size[rson[p]] + 1;} inline void right_rorate(int &p) { int tmp = lson[p]; lson[p] = rson[tmp]; rson[tmp] = p; size[tmp] = size[p]; pushup(p); p = tmp; } inline void left_rorate(int &p) { int tmp = rson[p]; rson[p] = lson[tmp]; lson[tmp] = p; size[tmp] = size[p]; pushup(p); p = tmp; } inline void insert(int &p, int x) { if(p == 0) { p = ++ cnt; size[p] = 1, w[p] = x, prio[p] = random_f(); return ; } size[p] ++; insert((x >= w[p]) ? rson[p] : lson[p], x); if(prio[lson[p]] < prio[p] && lson[p] != 0) right_rorate(p); if(prio[rson[p]] < prio[p] && rson[p] != 0) left_rorate(p); pushup(p); } inline void delete_f(int &p, int x) { size[p] --; if(w[p] == x) { if(lson[p] == 0 && rson[p] == 0) {p = 0; return ;} if(lson[p] == 0 || rson[p] == 0) {p = lson[p] + rson[p]; return ;} if(prio[lson[p]] < prio[rson[p]]) { right_rorate(p); delete_f(rson[p], x); return ; } else { left_rorate(p); delete_f(lson[p], x); return ; } } delete_f((x >= w[p]) ? rson[p] : lson[p], x); pushup(p); } inline int rank(int &p, int x) { if(p == 0) return 0; int tmp; if(x > w[p]) tmp = size[lson[p]] + 1 + rank(rson[p], x); else tmp = rank(lson[p], x); return tmp; } inline int select(int &p, int x) { if(x == size[lson[p]] + 1) return w[p]; if(x > size[lson[p]] + 1) return select(rson[p], x - size[lson[p]] - 1); else return select(lson[p], x); } inline int querypre(int &p, int x) { if(p == 0) return 0; // 找不到超过 x 的,就返回 0,tmp 的 0 是在这里赋值的 if(w[p] >= x) return querypre(lson[p], x); // 此时 w[p] < x,再到 p 的右子树里找一个最大的不超过 x 的,即为答案 int tmp = querypre(rson[p], x); return (tmp == 0) ? w[p] : tmp; } inline int querysuf(int &p, int x) { if(p == 0) return 0; if(w[p] <= x) return querysuf(rson[p], x); int tmp = querysuf(lson[p], x); return (tmp == 0) ? w[p] : tmp; }}treap;int main() { int n; scanf("%d", &n); treap.clear(); for(int i = 1; i <= n; i ++) { int opt, x; scanf("%d%d", &opt, &x); if(opt == 1) treap.insert(treap.rt, x); if(opt == 2) treap.delete_f(treap.rt, x); if(opt == 3) printf("%d\n", treap.rank(treap.rt, x) + 1); if(opt == 4) printf("%d\n", treap.select(treap.rt, x)); if(opt == 5) printf("%d\n", treap.querypre(treap.rt, x)); if(opt == 6) printf("%d\n", treap.querysuf(treap.rt, x)); } return 0;}
- 学习笔记 Treap
- 学习笔记:treap
- Treap学习笔记
- Treap 学习笔记
- Treap 学习笔记
- [中级数据结构学习笔记]一、Treap
- 平衡树(treap)学习笔记
- 平衡树:treap学习笔记(1)
- 非旋转 Treap 学习笔记(一)
- 平衡树:treap学习笔记(2)
- 非旋转 Treap 学习笔记(二)
- 平衡树:treap学习笔记(3)
- 平衡树学习笔记——旋转式treap
- 朴素treap学习
- Treap 基础学习
- Treap 学习总结
- Treap学习基本入门
- Treap树学习小结
- linux内核之中断和异常
- 分布式系统唯一ID生成方案
- php 单例模式
- 模块API之symbol_put_addr
- NOJ1322求子集重量之和(Calculate the sum of a subset's weight)
- Treap 学习笔记
- 61. Rotate List
- linux进程调度方法简述
- macaca之zfb
- Java程序员不可不知的几个网站,你去过几个?
- update通用生成SQL语句方法
- 初次尝试Maven+logj2.xml 配置
- 关于浏览器的请求数据到达后台乱码问题
- 练习(6)