博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
浅谈fhq treap
阅读量:5888 次
发布时间:2019-06-19

本文共 5015 字,大约阅读时间需要 16 分钟。

一、简介

fhq treap 与一般的treap主要有3点不同

1、不用旋转

2、以merge和split为核心操作,通过它们的组合实现平衡树的所有操作

3、可以可持久化

二、核心操作

代码中val表示节点权值,pri表示节点的优先级,维护小根堆

1、split

将1个treap分裂为两个treap

分裂主要有两种:以权值k作为分界点、以位置k作为分界点

①以权值k作为分界点

设原来的treap根节点为root,分裂后的<=k的treap A 的根节点为x,>k的treap B 的根节点为y

当前节点指针now从root开始,在treap A 的x位置加点,treap B 的y位置加点

若now的权值<=k,那么以now为根的左子树全部包含在A中,

把now给x,去now的右子树,x变成x的右子树,y还是y

若now的权值>k,那么以now为分的右子树全部包含在B中,

把now给y,去now的左子树,x还是x,y变成y的左子树

void split(int now,int k,int &x,int &y){    if(!now) x=y=0;    else    {        if(val[now]<=k)         {            x=now;            split(ch[now][1],k,ch[now][1],y);        }        else        {            y=now;            split(ch[now][0],k,x,ch[now][0]);        }        update(now);    }}

②以位置k作为分界点

设原来的treap根节点为root,分裂后的前k个节点的treap A 的根节点为x,剩下的节点的treap B 的根节点为y

判断放在哪颗treap的标准改成k和子树大小的比较,原理同上

void split(int now,int k,int &x,int &y){    if(!now) x=y=0;    else    {        if(k<=siz[ch[now][0]])        {            y=now;            split(ch[now][0],k,x,ch[now][0]);        }        else        {            x=now;            split(ch[now][1],k-siz[ch[now][0]]-1,ch[now][1],y);        }        update(now);    }}

2、merge

合并以x为根的treap A 和 以y为根的treap B

前提:A的权值全部<B的权值

注意在merge的过程中,维护好堆和二叉搜索树的性质

若优先级x<y,

维护堆的性质-->y成为x的子节点

维护二叉搜索树的性质-->y在x的右子树

若优先级x>y,

维护堆的性质-->x成为y的子节点

维护二叉搜索树的性质-->x在y的左子树

int merge(int x,int y){    if(!x || !y) return x+y;    if(pri[x]

三、单点操作

1、建树

建树有两种方式:nlogn 一个一个插、n 直接建

这里主要说后者

每次取mid作为当前子树的根节点即可

int new_node(int v){    siz[++tot]=1;    val[tot]=v;    pri[tot]=rand();    return tot;}int build(int l,int r){    if(l>r) return 0;    int mid=l+r>>1,v=val[mid];    int now=new_node(v);    ch[now][0]=build(l,mid-1);    ch[now][1]=build(mid+1,r);    update(now);    return now;}

2、插入

添加权值为v的点

1、按权值以v为分界点,split成两棵treapA(<=v),B( >v )

2、合并A和v,得到C

3、合并C和B

merge要求x的权值<y决定了 合并的顺序

split(root,v,x,y);root=merge(merge(x,new_node(v)),y);

3、删除

删除一个权值为v的点

1、按权值以v为分界点,split成两棵treap A(<=v),B( >v)

2、把A按权值以v-1为分界点,split成两棵treap C(<=v-1) ,D(=v)

3、合并D的根节点的左右子树,相当于删除根节点,得到treap E

4、合并C和E,得F

5、合并F和B

split(root,v,x,z);split(x,v-1,x,y);y=merge(ch[y][0],ch[y][1]);root=merge(merge(x,y),z);

4、查询v的排名

1、按权值以v-1为分界点,split成两棵treap A(<=v-1)  B(>=v)

2、A的大小+1 即为权值v的排名

3、合并A、B

split(root,v-1,x,y);cout<
<<'\n';root=merge(x,y);

5、查询排名为x的数

int get_kth(int now,int k){    while(1)    {        if(k<=siz[ch[now][0]]) now=ch[now][0];        else        {            k-=siz[ch[now][0]];            if(k==1) return now;            k--;            now=ch[now][1];        }    }}cout<
<<'\n';

6、查找比v小的最大的数(前驱)

1、按权值以v-1为分界点,split成两棵treap A(<=v-1),B(>=v)

2、在A中查找A的排名最靠后的数,输出

3、合并A和B

split(root,v-1,x,y);cout<
<<'\n';root=merge(x,y);

7、查询比v大的最小的数(后继)

1、按权值以v为分界点,split成两棵treap A(<=v),B(>v)

2、在B中找最靠前的数,输出

3、合并A和B

 

split(root,v,x,y);cout<
<<'\n';root=merge(x,y);

 

四、区间操作

对区间[l,r]执行某种操作

1、以位置r作为分界点,split成两棵treap A(前r个节点)、B(位置r之后的节点)

2、在A中以位置l-1为分界点,split成两棵treap C(前l-1个节点)、D(第l到第r个节点)

3、对D执行操作

注意因为执行区间操作,增加2个虚拟节点,最前面一个,最后面一个,所有节点后移一位

加虚拟节点的原因是 如果对区间[1,x]或[x,n]操作,那我们需要用到1-1 号 点和n+1号店

split(root,r+1,a,b);split(a,l,c,d);work(d)

五、模板

1、普通平衡树

#include
#include
#include
#include
using namespace std;#define N 100001int root;int tot;int fa[N],ch[N][2],val[N],pri[N],siz[N];void read(int &x){ x=0; int f=1; char c=getchar(); while(!isdigit(c)) { if(c=='-') f=-1; c=getchar(); } while(isdigit(c)) { x=x*10+c-'0'; c=getchar(); } x*=f;}void update(int x){ siz[x]=siz[ch[x][0]]+siz[ch[x][1]]+1;}int new_node(int v){ siz[++tot]=1; val[tot]=v; pri[tot]=rand(); return tot;}int merge(int x,int y){ if(!x || !y) return x+y; if(pri[x]
View Code

2、文艺平衡树

#include
#include
#include
#include
using namespace std;#define N 100005int n;int tot,root;int siz[N],ch[N][2],val[N],pri[N];bool rev[N];void read(int &x){ x=0; int f=1; char c=getchar(); while(!isdigit(c)) { if(c=='-') f=-1; c=getchar(); } while(isdigit(c)) { x=x*10+c-'0'; c=getchar(); } x*=f;}int new_node(int v){ siz[++tot]=1; val[tot]=v; pri[tot]=rand(); return tot;}void update(int x){ siz[x]=1+siz[ch[x][0]]+siz[ch[x][1]];}int build(int l,int r){ if(l>r) return 0; int mid=l+r>>1,v=mid-1; int now=new_node(v); ch[now][0]=build(l,mid-1); ch[now][1]=build(mid+1,r); update(now); return now;}void down(int x){ rev[x]^=1; swap(ch[x][0],ch[x][1]); rev[ch[x][0]]^=1; rev[ch[x][1]]^=1;}int merge(int x,int y){ if(!x || !y) return x+y; if(rev[x]) down(x); if(rev[y]) down(y); if(pri[x]
=1 && val[x]<=n) cout<
<<' '; if(ch[x][1]) dfs(ch[x][1]);}int main(){ srand(time(0)+20001024); int m; read(n); read(m); root=build(1,n+2); int l,r; while(m--) { read(l); read(r); reverse(l,r); } dfs(root);}
View Code

 

转载于:https://www.cnblogs.com/TheRoadToTheGold/p/8288818.html

你可能感兴趣的文章
cocos2d中CCAnimation的使用(cocos2d 1.0以上版本)
查看>>
【吉光片羽】短信验证
查看>>
gitlab 完整部署实例
查看>>
GNS关于IPS&ASA&PIX&Junos的配置
查看>>
影响企业信息化成败的几点因素
查看>>
SCCM 2016 配置管理系列(Part8)
查看>>
struts中的xwork源码下载地址
查看>>
ABP理论学习之仓储
查看>>
我的友情链接
查看>>
Tengine新增nginx upstream模块的使用
查看>>
CentOS图形界面和命令行切换
查看>>
HTML5通信机制与html5地理信息定位(gps)
查看>>
汽车常识全面介绍 - 悬挂系统
查看>>
加快ALTER TABLE 操作速度
查看>>
学习笔记之软考数据库系统工程师教程(第一版)
查看>>
PHP 程序员的技术成长规划
查看>>
memcached 分布式聚类算法
查看>>
jquery css3问卷答题卡翻页动画效果
查看>>
$digest already in progress 解决办法——续
查看>>
mysql 数据类型
查看>>