浅谈无旋treap(fhq_treap)
一、简介
无旋Treap(fhq_treap),是一种不用旋转的treap,其代码复杂度不高,应用范围广(能代替普通treap和splay的所有功能),是一种极其强大的平衡树。
无旋Treap是一个叫做范浩强的大佬发明的(快%啊!)
在我们一起学习无旋Treap之前,本蒟蒻有几句活想说:
1.无旋Treap我个人认为是最容易理解的一种平衡树,而且编程复杂度不高,功能还那么强大。
我一开始学平衡树的时候是先从普通的带旋转的Treap开始学的。那种Treap,我现在都没搞懂什么左旋右旋究竟是怎么一回事。
我当时真的以为我这辈子都不指望学会平衡树了,直到djq大佬(快%!)告诉了我这种很美妙的数据结构。
我真就不明白了,为什么那么多人愿意去学普通的Treap而不学无旋Treap。无旋Treap更容易理解,最主要的是他与普通的Treap相比能可持久化!(尽管我不会)
所以,在大家学习完无旋Treap以后,本蒟蒻请诸君不妨推广一下无旋Treap,造福更多的Oier。
2.关于无旋Treap和其他平衡树的比较:(这个建议大家学完无旋Treap再来看,可能感触会更深刻一些)
与AVL相比:旋转操作真的很浪费时间,最坏情况下复杂度为O(log n),而且AVL树难写无比,不适合运用于算法竞赛。
与普通Treap相比:参见第一条
与splay相比:基本上可以代替splay的所有功能,但是在处理LCT问题上没有splay优秀
与rbt(红黑树相比):红黑树特别难写是众所周知的。
与sbt相比:sbt是我认为的最强大的平衡树。但是无旋Treap中的merge、split操作的应用的广泛(可持久化Treap维护Hash之类的)是sbt做不到的。
二、Treap
什么是Treap?
Treap=Tree+heap
相信大家都知道二叉堆吧。父节点的权值比子节点都要大(或小)
而Treap,则是在BST(二叉查找树)的基础上,添加二叉堆中的这个元素。
Treap与heap的区别是,heap是完全二叉树,而Treap不是。
下面的
三、核心操作
我在前面说过,普通的Treap最烦人的地方便是旋转。
而无旋Treap是如何做到无旋的呢?
关键就在两个操作:merge和split
1.split
split,顾名思义,就是把一个平衡树分成两棵树。
split有两种:一种是按照权值split,一种是按照size来split。
如果按照权值split,那么分出来两棵树的第一棵树上的每一个数的大小都小于(或小于等于,视具体情况而定)x;
如果按照size split,那么分出来两棵树的第一棵树恰好有x个节点。
我们可以结合具体代码讲解:
inline void split(int k,int& l,int& r,int x){//理解时,我们可以把l当做是答案的第一个,r当做答案的第二个,这个函数的意义是:将以k为根的树按照val分为以l为根的树和以r为根的树。注意引用的作用
if(!k){
l=r=;//分到底了,返回
return;
}
if(tree[k].val<x){//如果比它小
l=k;//那么x肯定在k的右子树里,先将k贴到第一个答案上
split(tree[l].r,tree[l].r,r,x);//把第一个答案的右子树按x分开,得到答案(这里自己理解一下,不难懂)
}else{//反之亦然
r=k;
split(tree[r].l,l,tree[r].l,x);
}
push_up(k);
}
而按size split的道理是一样的:
inline void split(int k,int& l,int& r,int x){
if(!k){
l=r=;
return;
}
if(tree[tree[k].l].size+<=x){
l=k;
split(tree[l].r,tree[l].r,r,x-tree[tree[k].l].size-);//注意这里有些变化
}else{
r=k;
split(tree[r].l,l,tree[r].l,x);
}
push_up(k);
}
这样就把一棵树分开了。
2.merge
merge就是把两颗原本分开的树合并在一起。
我们仍然结合具体代码讲解
inline void merge(int& k,int l,int r){//函数名的意义是:把以l为根的树和以r为根的树合并为以k为根的树
if(!l||!r){//合并到底,返回
k=l+r;
return;
}
if(tree[l].p>tree[r].p){//默认大根堆
k=l;//先把l挂到k上
merge(tree[k].r,tree[k].r,r);//注意要满足BST性质
}else{//反之亦然
k=r;
merge(tree[k].l,l,tree[k].l);
}
push_up(k);
}
有了merge和split,其他平衡树的基本操作就好做多了
三、其他操作
inline void insert(int val){
int x,y;
split(root,x,y,val-);
merge(x,x,New(val));
merge(root,x,y);
}
inline void Delete(int val){
int x,y,z;
split(root,x,y,val);
split(x,x,z,val-);
merge(z,tree[z].l,tree[z].r);
merge(x,x,z);
merge(root,x,y);
}
inline int rnk(int val){
int x,y;
split(root,x,y,val-);
int ans=tree[x].size+;
merge(root,x,y);
return ans;
}
inline int kth(int k){
int x=root;
while(true){
if(k==tree[tree[x].l].size+) return tree[x].val;
if(k<=tree[tree[x].l]) x=tree[x].l;
else k-=tree[tree[x].l].size+,x=tree[x].r;
}
}
inline int pre(int val){
int x,y;
split(root,x,y,val-);
int ans=tree[x].val;
merge(root,x,y);
return ans;
}
inline int suf(int val){
int x,y;
split(root,x,y,val);
int ans=kth(tree[x].size+);
merge(root,x,y);
return ans;
}
最后给大家放上一道模板题P3369 【模板】普通平衡树
如有不足请指正,谢谢
浅谈无旋treap(fhq_treap)的更多相关文章
- [转载]无旋treap:从单点到区间(例题 BZOJ1500&NOI2005 维护数列 )
转自ZZH大佬,原文:http://www.cnblogs.com/LadyLex/p/7182631.html 1500: [NOI2005]维修数列 Time Limit: 10 Sec Mem ...
- [转载]无旋treap:从好奇到入门(例题:bzoj3224 普通平衡树)
转载自ZZH大佬,原文:http://www.cnblogs.com/LadyLex/p/7182491.html 今天我们来学习一种新的数据结构:无旋treap.它和splay一样支持区间操作,和t ...
- [您有新的未分配科技点]无旋treap:从好奇到入门(例题:bzoj3224 普通平衡树)
今天我们来学习一种新的数据结构:无旋treap.它和splay一样支持区间操作,和treap一样简单易懂,同时还支持可持久化. 无旋treap的节点定义和treap一样,都要同时满足树性质和堆性质,我 ...
- Luogu 3369 / BZOJ 3224 - 普通平衡树 - [无旋Treap]
题目链接: https://www.lydsy.com/JudgeOnline/problem.php?id=3224 https://www.luogu.org/problemnew/show/P3 ...
- 【算法学习】Fhq-Treap(无旋Treap)
Treap——大名鼎鼎的随机二叉查找树,以优异的性能和简单的实现在OIer们中广泛流传. 这篇blog介绍一种不需要旋转操作来维护的Treap,即无旋Treap,也称Fhq-Treap. 它的巧妙之处 ...
- 无旋treap的区间操作实现
最近真的不爽...一道维修数列就做了我1上午+下午1h+1晚上+晚上1h+上午2h... 一道不错的自虐题... 由于这一片主要讲思想,代码我放这里了 不会无旋treap的童鞋可以进这里 呵呵... ...
- 无旋treap的简单思想以及模板
因为学了treap,不想弃坑去学splay,终于理解了无旋treap... 好像普通treap没卵用...(再次大雾) 简单说一下思想免得以后忘记.普通treap因为带旋转操作似乎没卵用,而无旋tre ...
- [BZOJ3223]文艺平衡树 无旋Treap
3223: Tyvj 1729 文艺平衡树 Time Limit: 10 Sec Memory Limit: 128 MB Description 您需要写一种数据结构(可参考题目标题),来维护一个 ...
- [您有新的未分配科技点] 无旋treap:从单点到区间(例题 BZOJ1500&NOI2005 维护数列 )
1500: [NOI2005]维修数列 Time Limit: 10 Sec Memory Limit: 64 MB Description Input 输入的第1 行包含两个数N 和M(M ≤20 ...
随机推荐
- 基于【 springBoot+jsoup】一 || 爬取全国行政区划数据
一.代码演示 如果中途中断,可进行刷选过滤已拉取省份数据 /** * TODO * * @author kevin * @createTime 2019-11-18 19:37 */ @RestCon ...
- English-培训1-Phonetic symbols
- JavaScript中进制和字符编码问题
1.进制: JavaScript中允许使用字面量的形式声明不同进制的数字: var a = 0b10; // 2 声明一个二进制 var b = 010; // 8 八进制,严格模式下会报错 var ...
- C#基础 - 定义变量,输入输出
本节课主要讲解C#的发展历史及部分C#语言基础, 主要内容有:控制台程序的创建,输出,输入,定义变量,变量赋值,值覆盖,值拼接,值 打印,两种数据类型,整形类型转换 本节重点难点:定义变量 ...
- LInux CentOS7 vsftpd 配置注释
本文首发: https://www.somata.work/2019/LinuxCentOSvsftpdConfigComment.html vsftpd.conf anonymous_enable= ...
- 如何写好demo——学习感悟
文章标题:教你如何写好Demo应用 如何制作出最有用的demo呢? 简,易 在demo中,我们要专注于单一的主题.我们的教学覆盖了很大的知识范围,因此,化整为零是非常必要的. 例如,我们要说明Andr ...
- 基于Java+Selenium的WebUI自动化测试框架(二)-----页面操作接口
在有了基础的Position类之后,我们需要考虑我们在寻找完页面元素之后,需要做什么.这个“做”什么,可以理解为我们在页面上需要对应的一系列动作.比如:点击,输入,切换窗口,寻找元素,判断元素是否存在 ...
- 【转】oracle的 分表 详解 -----表分区
转载:https://www.cnblogs.com/congcidaishangjiamianju/p/8045804.html 一 表空间及分区表的概念 表空间: 是一个或多个数据文件的集合,所有 ...
- 05-Docker私有仓库
一.介绍私有仓库顾名思义,如果我们不想把docker镜像公开放到公有仓库中,只想在部门或团队内部共享docker镜像,这时私有仓库就来了. 二.私有仓库搭建与配置1.拉取私有仓库镜像,这里说明一下,私 ...
- TOMCAT 请求HTTP原理
一.Tomcat是什么?Tomcat是一个Web应用服务器,同时也是一个Servlet/JSP容器.Tomcat作为Servlet容器,负责处理客户端请求,把请求传送给Servlet,并将Servle ...