FHQ-Treap小结
\(Orz\) 范浩强大爷,竟然搞出了如此夺天地造化的数据结构.
\(FHQ-Treap\),又名非旋\(Treap\),是范浩强大爷在某些奇特的灵感之下发明的一种平衡树,因其与\(Treap\)相似的性质和无旋转操作的特性被称为非旋\(Treap\).非旋\(Treap\)用\(Split\)和\(merge\)两个操作维护平衡,其本质是堆的合并.(左偏树?)
前置函数&结构体:
#define Drt pair < Treap * , Treap * >
struct Treap {
Treap * son[2] ;
int val , size , rank ;
Treap (int val) : val ( val ) { son[0] = son[1] = NULL ; size = 1 ; rank = rand () ; }
inline void maintain () {
this->size = 1 ;
if ( this->son[0] != NULL ) this->size += this->son[0]->size ;
if ( this->son[1] != NULL ) this->size += this->son[1]->size ;
return ;
}
} * root = NULL ;
inline int siz (Treap * rt) { return rt == NULL ? 0 : rt->size ; }
我们先来介绍一下\(FHQ-Treap\)的灵魂之一:\(Split\)操作
\(Split(rt,k)\) 表示把以 \(rt\) 为根的树中的前\(k\)个元素分裂出来,其返回值为一个二元组表示分裂出来的两棵树的根
也十分好写
\(Code:\)
inline Drt Split (Treap * rt , int k) {
if ( rt == NULL ) return Drt ( NULL , NULL ) ;
Drt t ;
if ( k <= siz ( rt->son[0] ) ) { // 如果说需分裂的前k个都在左子树,直接递归处理
t = Split ( rt->son[0] , k ) ; rt->son[0] = t.second ; // 把左子树中残余的元素和右子树的元素放到一起即为第二棵新树
rt->maintain () ; t.second = rt ;
} else { // 如果需分裂的前k个不只在左子树,递归向右子树分裂需要的元素个数
t = Split ( rt->son[1] , k - siz ( rt->son[0] ) - 1 ) ;
rt->son[1] = t.first ; t.first = rt ; // 把右子树中的残余前k个拿出来,和左子树一起组成第一棵新树
}
return t ;
}
这样,就完成了\(Split\) 操作.
再来看\(merge\) 操作
\(merge(x,y)\) 表示把以\(x\)为根的树和以\(y\)为根的树合并,返回值为新树的根
也并不难写
\(Code:\)
inline Treap * merge (Treap * x , Treap * y) {
if ( x == NULL ) return y ; if ( y == NULL ) return x ; // 显然
if ( x->rank < y->rank ) { // 类似于启发式合并
x->son[1] = merge ( x->son[1] , y ) ;
x->maintain () ; return x ;
} else {
y->son[0] = merge ( x , y->son[0] ) ;
y->maintain () ; return y ;
}
}
这就是非旋\(Treap\)的两个核心操作,当然还有一些比较重要的基础操作,直接放上吧,也不难写
\(Getrank(rt,key)\) 表示在以\(rt\)为根的树中查询\(key\)的排名(名次树功能之一)
inline int Getrank (Treap * rt , int key) {
if ( rt == NULL ) return 0 ;
else if ( key <= rt->val ) return Getrank ( rt->son[0] , key ) ;
else return Getrank ( rt->son[1] , key ) + siz ( rt->son[0] ) + 1 ;
}
\(Getkth(rt,key)\)表示在以\(rt\)为根的子树中查询排名为\(key\)的元素(名次树功能之二)
inline int Getkth (Treap * & rt , int key) { // 这里有对根的修改
if ( rt == NULL ) return 0 ;
Drt x = Split ( rt , key - 1 ) ; // 把前key-1个数字分裂出来
Drt y = Split ( x.second , 1 ) ; // 这时剩下的树中的第一个元素即为所求
Treap * t = t.first ; // 先记下来
rt = merge ( x.first , merge ( t , y.second ) ) ; // 别忘了合并回去
return t == NULL ? 0 : t->val ;
}
\(insert(rt,key)\)表示在以\(rt\)为根的树中插入\(key\)这个元素
inline void insert (Treap * & rt , int key) {
int k = Getrank ( rt , key ) ; Drt t = Split ( rt , k ) ; // 找出该插入的位置
Treap * node = new Treap ( key ) ; // 创建新节点(只有根的树)
rt = merge ( t.first , merge ( node , t.second ) ) ; // 把新节点合并回去
return ;
}
\(remove(rt,key)\)表示在以\(rt\)为根的树中删除\(key\)这个元素
inline void remove (Treap * & rt , int key) {
int k = Getrank ( rt , key ) ; Drt x = Split ( rt , k ) ; // 把应该删除的拿出来
Drt y = Split ( x.second , 1 ) ; delete ( y.first ) ; // 删去
rt = merge ( x.first , y.second ) ; return ; // 再合并回去
}
前驱和后继也比较简单
前驱就是:
Getkth ( root , Getrank ( root , key ) ) ;
后继就是:
Getkth ( root , Getrank ( root , key + 1 ) + 1 )
这几个相信各位一看就懂,也就不再赘述了.
我个人认为\(FHQ-Treap\)是最容易理解的平衡树,而且码量也较小,很适合像博主这样的蒟蒻学习.
\(Added:\)
最近又发现了 \(FHQTreap\) 的另一种 \(Split\) 的方式:按权值分裂
就是把整棵树按照权值分裂成一半比 \(key\) 大的,一半比 \(key\) 小的
有的时候,这种分裂有奇效
\(Code:\)
inline Drt SplitV ( Treap * rt , int key ) {
if ( rt == NULL ) return Drt ( NULL , NULL ) ;
Drt t ;
if ( rt->val <= key ) {
t = SplitV ( rt->son[1] , key ) ; rt->son[1] = t.first ;
rt->maintain () ; t.first = rt ;
} else {
t = SplitV ( rt->son[0] , key ) ; rt->son[0] = t.second ;
rt->maintain () ; t.second = rt ;
}
return t ;
}
FHQ-Treap小结的更多相关文章
- FHQ Treap小结(神级数据结构!)
首先说一下, 这个东西可以搞一切bst,treap,splay所能搞的东西 pre 今天心血来潮, 想搞一搞平衡树, 先百度了一下平衡树,发现正宗的平衡树写法应该是在二叉查找树的基础上加什么左左左右右 ...
- fhq treap最终模板
新学习了fhq treap,厉害了 先贴个神犇的版, from memphis /* Treap[Merge,Split] by Memphis */ #include<cstdio> # ...
- NOI 2002 营业额统计 (splay or fhq treap)
Description 营业额统计 Tiger最近被公司升任为营业部经理,他上任后接受公司交给的第一项任务便是统计并分析公司成立以来的营业情况. Tiger拿出了公司的账本,账本上记录了公司成立以来每 ...
- 【POJ2761】【fhq treap】A Simple Problem with Integers
Description You have N integers, A1, A2, ... , AN. You need to deal with two kinds of operations. On ...
- 【fhq Treap】bzoj1500(听说此题多码上几遍就能不惧任何平衡树题)
1500: [NOI2005]维修数列 Time Limit: 10 Sec Memory Limit: 64 MBSubmit: 15112 Solved: 4996[Submit][Statu ...
- 「FHQ Treap」学习笔记
话说天下大事,就像fhq treap —— 分久必合,合久必分 简单讲一讲.非旋treap主要依靠分裂和合并来实现操作.(递归,不维护fa不维护cnt) 合并的前提是两棵树的权值满足一边的最大的比另一 ...
- FHQ Treap摘要
原理 以随机数维护平衡,使树高期望为logn级别 不依靠旋转,只有两个核心操作merge(合并)和split(拆分) 因此可持久化 先介绍变量 ; int n; struct Node { int v ...
- 在平衡树的海洋中畅游(四)——FHQ Treap
Preface 关于那些比较基础的平衡树我想我之前已经介绍的已经挺多了. 但是像Treap,Splay这样的旋转平衡树码亮太大,而像替罪羊树这样的重量平衡树却没有什么实际意义. 然而类似于SBT,AV ...
- 浅谈fhq treap
一.简介 fhq treap 与一般的treap主要有3点不同 1.不用旋转 2.以merge和split为核心操作,通过它们的组合实现平衡树的所有操作 3.可以可持久化 二.核心操作 代码中val表 ...
- fhq treap 学习笔记
序 今天心血来潮,来学习一下fhq treap(其实原因是本校有个OIer名叫fh,当然不是我) 简介 fhq treap 学名好像是"非旋转式treap及可持久化"...听上去怪 ...
随机推荐
- sql中检查时间是否重叠
先画一个时间轴,方便理解. 设新的时间块,开始时间为@BeginDate,结束时间为@EndDate.数据库中的数据为BeginDate和EndDate 这样可以直观的看出来,新的时间块插入进来,只需 ...
- 使用linux backtrace打印出错函数堆栈信息
一般察看函数运行时堆栈的方法是使用GDB(bt命令)之类的外部调试器,但是,有些时候为了分析程序的BUG,(主要针对长时间运行程序的分析),在程序出错时打印出函数的调用堆栈是非常有用的. 在glibc ...
- magento-2.2.6-1VM环境镜像-沙箱 - - 完全隔离的环境
打包处理下载地址: 链接:https://pan.baidu.com/s/1HX0WjWEN8Wc-4TDvgEWMog 提取码:s2ls 官方下载 文档 BITNAMI MAGENTO堆栈虚拟机 B ...
- 【BZOJ4013】[HNOI2015]实验比较(动态规划)
[BZOJ4013][HNOI2015]实验比较(动态规划) 题面 BZOJ 洛谷 题解 看题目意思就是给你一棵树,连边表示强制顺序关系.然后你要给点染色,在满足顺序关系的情况下,将序列染成若干个颜色 ...
- CF700E E. Cool Slogans
https://codeforces.com/contest/700/problem/E 题解:https://www.luogu.org/problemnew/solution/CF700E 其实就 ...
- 一加X 手机变砖过程
很久无使用的一台手机,因需要获得ROOT权限而使用kingroot这款软件,无法获得root权限.而这个软件会导致手机无法进入ERCOVERY模式,后果是你没有办法刷入别的rom,而kingroot的 ...
- 通用的C#导出Excel 支持2003及2007以上版本
将数据导出到Excel,供用户下载是经常要使用到的一个功能,我这里进行了一些封装,目前已满足项目中导出的需求,使用DataReader导出Excel,支持自定义多表头,使用委托处理字段格式化及字段值的 ...
- nginx日志格式定义和nginx.conf配置模板说明
在http的功能里添加log_format模块,内容如下: log_format main escape=json '{ "@timestamp": "$time_iso ...
- 2018-2019-2 20165232 《网络对抗技术》 Exp6 信息搜集与漏洞扫描
2018-2019-2 20165232 <网络对抗技术> Exp6 信息搜集与漏洞扫描 一.实践目标 掌握信息搜集的最基础技能与常用工具的使用方法. 二.实践内容. 各种搜索技巧的应 D ...
- linux 命令之文件读取,head, tail, tailf, sed
head 看文件的前100行head -100 filename tail/tailf查看文件的后100行tail -100 filename 或 tail -n 100 filename ta ...