FHQ-treap 即非旋Treap,是一种短小精悍,功能丰富的平衡树。

据说它的效率介于 Treap 和 Splay 之间(可能是我的FHQ常数比较小,跑得比我的Treap还快)。

它可以实现 Splay 可以实现的所有功能,包括平衡树的基本操作和区间翻转操作。

它的实现难度比 Splay 要简单很多,没有 Splay 那么多转来转去的操作,不会令人头晕,而且FHQ代码的对称性良好,易于调试。

有两种 FHQ-Treap,一种是按值为关键字的,一种是按下标为关键字的(适用于区间翻转等操作时)。

FHQ-Treap 类似于Treap,都有一个随机的 key 值,以此来保持树的平衡。

Split (分裂) & merge (合并)

FHQ-Treap 的核心操作为 split \text{split} split (分裂) 和 merge \text{merge} merge (合并)。

split ( r o o t , v a l , x , y ) \text{split}(root,val,x,y) split(root,val,x,y) 表示将 r o o t root root 的子树拆分为两半,一半中的值(或下标)都小于等于 v a l val val, 一半的值都大于 v a l val val。

merge ( x , y ) \text{merge}(x,y) merge(x,y) 表示将 x x x 子树和 y y y 子树合并起来,要保证x中的元素都小于y中的元素。

具体操作都是依靠 split \text{split} split 和 merge \text{merge} merge 来实现的。

// 按值为关键字
struct Node
{int l,r; // 左右儿子编号int val;// 真实值int key;// 随机值int size;// 子树大小
}fhq[N];void pushup(int u){ // 维护其子树大小fhq[u].size = fhq[fhq[u].l].size + fhq[fhq[u].r].size + 1;
}inline void split(int u,int val,int &x,int &y) // 递归实现
{if(!u) {// 空节点x = y = 0;return;}if(fhq[u].val <= val) { // 点的值大于val,递归其右儿子x = u;split(fhq[u].r,val,fhq[u].r, y);}else { // 点的值小于val,递归其左儿子y = u;split(fhq[u].l,val,x,fhq[u].l);}pushup(u); // 维护其子树大小
}
inline int merge(int x,int y)
{if(!x || !y) return x | y;  // 有一个子树为空,不需要操作if(fhq[x].key >= fhq[y].key) { // key 为随机值,这里是保证平衡的关键fhq[x].r = merge(fhq[x].r,y); // 将y接到右儿子或更下方上,递归合并pushup(x);return x;}else {fhq[y].l = merge(x,fhq[y].l);pushup(y);return y;}
}

insert (插入)

// 插入一个值为 val 的点
int create(int val) { // 新建一个值为val的点fhq[++cnt].val = val,fhq[cnt].key = rnd(),fhq[cnt].size = 1;return cnt;
}
void insert(int val){ split(root,val,x,y); // 按val分裂成x,y,x中的元素都小于val,以此满足merge需要的性质root = merge(merge(x,create(val)), y); // 将x,新建的值为val的点,y合并
}

删除

//删除一个值为val的点
void del(int val) {split(root,val,x,z);split(x,val-1,x,y); // 前面两行将val点(y)抠出来y = merge(fhq[y].l,fhq[y].r); // 直接合并y的左节点和右节点,即为删除yroot = merge(merge(x,y),z); // 合并即可
}

按值获取排名

//找到val在树中的排名
void getrank(int val) {split(root,val-1,x,y); // 抠出小于val的那些点(x)printf("%d\n",fhq[x].size+1); // x的size即为小于val的点的个数, 加上val自己即为排名root = merge(x,y);
}

按排名获取值

//获取排名为rank的点的值
void getval(int rank) {int u = root;while(u) {// fhq[fhq[u].l].size为值小于点u的点的个数if(fhq[fhq[u].l].size + 1 == rank) break; // 找到了!!!if(fhq[fhq[u].l].size >= rank) u = fhq[u].l; // 发现rank在左儿子中,递归查找左儿子else rank -= fhq[fhq[u].l].size + 1, u = fhq[u].r; // 发现rank在右儿子中,递归查找右儿子前要减去rank的值}printf("%d\n", fhq[u].val);
}

找前驱

// 找到小于val的最大值
void pre(int val) {split(root,val-1,x,y); // 先抠出小于val的值(x)int u = x;while(fhq[u].r) u = fhq[u].r; // 在小于val的值中找最大值printf("%d\n",fhq[u].val);root = merge(x,y);
}

找后继

// 找大于val的最小值
void nxt(int val) {split(root,val,x,y); // 先抠出大于val的值(y)int u = y;while(fhq[u].l) u = fhq[u].l; // 在大于val的值中找最小值printf("%d\n",fhq[u].val);root = merge(x,y);
}

完整板子

点击查看代码

// P3369 【模板】普通平衡树
// 因为这里加了两个哨兵,所以实现和上面介绍的有一点点区别
#include <bits/stdc++.h>
using namespace std;const int N = 1e5 + 10;struct Node {int l,r;int val;// real valueint key;// for treapint size;
} fhq[N];
int root,cnt;mt19937 rnd(233);int create(int val) {fhq[++cnt].val = val,fhq[cnt].key = rnd(),fhq[cnt].size = 1;return cnt;
}void pushup(int u) {fhq[u].size = fhq[fhq[u].l].size + fhq[fhq[u].r].size + 1;
}void split(int u,int val,int &x,int &y) {if(!u) {x = y = 0;return;}if(fhq[u].val <= val) {x = u;split(fhq[u].r,val,fhq[u].r, y);} else {y = u;split(fhq[u].l,val,x,fhq[u].l);}pushup(u);
}int merge(int x,int y) {if(!x || !y) return x + y;if(fhq[x].key >= fhq[y].key) {fhq[x].r = merge(fhq[x].r,y);pushup(x);return x;} else {fhq[y].l = merge(x,fhq[y].l);pushup(y);return y;}
}int x,y,z;
void insert(int val) {split(root,val,x,y);root = merge(merge(x,create(val)), y);
}void del(int val) {split(root,val,x,z);split(x,val-1,x,y);y = merge(fhq[y].l,fhq[y].r);root = merge(merge(x,y),z);
}void getrank(int val) {split(root,val-1,x,y);printf("%d\n",fhq[x].size);root = merge(x,y);
}void getval(int rank) {rank++;int u = root;while(u) {if(fhq[fhq[u].l].size + 1 == rank) break;if(fhq[fhq[u].l].size >= rank) u = fhq[u].l;else rank -= fhq[fhq[u].l].size + 1, u = fhq[u].r;}printf("%d\n", fhq[u].val);
}void pre(int val) {split(root,val-1,x,y);int u = x;while(fhq[u].r) u = fhq[u].r;printf("%d\n",fhq[u].val);root = merge(x,y);
}void nxt(int val) {split(root,val,x,y);int u = y;while(fhq[u].l) u = fhq[u].l;printf("%d\n",fhq[u].val);root = merge(x,y);
}int main() {int n;scanf("%d",&n);insert(2147483647), insert(-2147483647); // 哨兵while(n--) {int opt,val;scanf("%d%d",&opt,&val);if(opt == 1) insert(val);if(opt == 2) del(val);if(opt == 3) getrank(val);if(opt == 4) getval(val);if(opt == 5) pre(val);if(opt == 6) nxt(val);}return 0;
}

FHQ-Treap 简介相关推荐

  1. P2596 [ZJOI2006]书架(fhq treap)

    P2596 [ZJOI2006]书架 我们用fhq treap来完成这一题 对于一个新插入的节点我们取权值为其索引值,其所记录的valuevaluevalue是其当前索引所在位置. 操作一:把索引为v ...

  2. 可持久化平衡树(FHQ Treap)

    两个最基本的操作 merge合并 split分割 merge 把两棵treap合并成一棵treap,要满足T1最大值要比T2最小值小,比较将随机数值key值更大的作为合并后的根 假设T1作为根节点作为 ...

  3. FHQ Treap 详解

    0. 简单介绍 FHQ Treap,以下简写为fhq,是一种treap(树堆)的变体,功能比treap强大,代码比splay好写,易于理解,常数稍大. fhq不需要通过一般平衡树的左右旋转来保持平衡, ...

  4. FHQ Treap 总结

    [前言(一堆废话)] 目前 OI 竞赛中两大主流平衡树之一就是 FHQ Treap(另一个是 Splay). 普通 BST 的中序遍历中,val 值构成一个单调递增的序列. Treap 在 BST 的 ...

  5. Treap + FHQ Treap

    一:Treap 支持:插入.删除.查询 x x x 的排名.查询排名为 x x x 的数.查前驱.查后继.左旋右旋 模板题:P3369 [模板]普通平衡树 #include<bits/stdc+ ...

  6. 平衡树学习笔记之 fhq Treap

    平衡树学习笔记 1:fhq Treap(非旋 Treap) 正文开始前首先 %%% fhq 大佬. 众所周知,平衡树是一种 非常猥琐 码量堪忧的数据结构. 他的祖先是一种叫做二叉搜索树 ( B S T ...

  7. FHQ TREAP 学习总结

    F H Q T R E A P FHQ \ TREAP FHQ TREAP P r e p a r e K n o w l e d g e Prepare \ Knowledge Prepare Kn ...

  8. 【模板】FHQ Treap

    FHQ Treap 无旋Treap 维护的信息 c h [ i ] [ 0 / 1 ] ch[i][0/1] ch[i][0/1] v a l [ i ] val[i] val[i] f i x [ ...

  9. FHQ Treap【基于P3369的讲解】【随机数、各数组、函数运用】

    该模板题的题目链接 很多人看到了FHQ Treap都不知道它是干什么用的,今天也是刚学的FHQ Treap,学了一整天了,终于过掉了洛谷的P3369了,也算是对这个算法有了些自己的了解,还是错的太多次 ...

  10. FHQ Treap学习记录(详解)

    前言:嘻嘻,本蒟蒻的第一篇文章!由于本蒟蒻第一次写博客,本文章质量可能不是很好QAQ 前置芝士(了解即可啦~):C++.BST 二叉搜索树.堆.二叉堆 如果您不想听蒟蒻胡扯 Treap,可以直接根据目 ...

最新文章

  1. 批量关闭公众号推送_微信推出“一键拒收”长期未读公众号推送功能
  2. 【译】使用Kotlin和RxJava测试MVP架构的完整示例 - 第1部分
  3. UML-2-迭代、进化和敏捷
  4. Docker容器间通讯,直接路由方式实现网络通讯
  5. SwiftUI5.x微博入门案例第1部分
  6. BugkuCTF-MISC题convert
  7. STM32H743+CubeMX-定时器TIM输出PWM(Out Compare模式) + 中断
  8. Android之SurfaceView学习(一)
  9. 安卓手机老是自动保存图片_Redmi K30 Pro自动亮度调节和iPhone基本一致,安卓手机的大进步...
  10. 投资顾问模式将改变整个基金行业
  11. Python实现8中常用排序算法
  12. pycharm汉化包使用后无法打开设置选项问题
  13. TLPI UNIX linux系统编程手册源代码运行
  14. 数据分析的 5 种归纳方法
  15. VS Code的相对路径问题 (No such file or directory: './lenna.png')
  16. 淘宝接口 http://ip.taobao.com/service/getIpInfo.php?ip=myip 获取不到手机ip地址
  17. 华为云-- 消息通知服务(SMN)
  18. 八评腾讯:解密腾讯的中年危机
  19. 盒子滚动到底部有偏差 js_干货丨JS 经典实例收集整理
  20. 立下19年的flag,跪着也要完成!

热门文章

  1. 继机器人披萨后,我又吃了个机器人汉堡!
  2. nn.BCELoss和nn.CrossEntropyloss
  3. 河南省旅游服务中心信息中心备份及集成
  4. 规则引擎 Drools--决策表(Decision Table)使用简介
  5. Qt源码解析之QThread
  6. 样本方差与总体方差的区别
  7. cad调了比例因子没反应_「室内设计」大神们都在用的9个CAD制图技巧,你会用几个?...
  8. camera 之 createCaptureSession
  9. 电气火灾监控系统在某市新能源有轨电车示范线工程的应用
  10. matlab应力分析,MATLAB有限元分析与应用概述.ppt