FHQ Treap
前言:
平衡树是一种二叉搜索树,通过一些方法来做到快速维护单点或区间信息和快速查询单点或区间信息,其中包括排名、前驱等等。在
FHQ Treap:
FHQ Treap 是一种不带旋操作的平衡树,使用分裂和合并为基础操作,以此来维护树的平衡性。
节点操作:
节点定义:
这没啥好讲的。
struct node{ int ls,rs;//左右儿子 int val,key;// 值与键值 int siz;// 子树大小 }fhq[N]; int root,cnt;// 根节点编号和节点数量
新建节点:
inline int newnode(int val){// 返回新节点编号 fhq[++cnt].val=val; fhq[cnt].key=rnd();//这里用的是 std::mt19937 rnd(114514) fhq[cnt].siz=1; return cnt; }
更新节点信息:
inline void update(int o){ fhq[o].siz=fhq[fhq[o].ls].siz+fhq[fhq[o].rs].siz+1; }
基础操作:
分裂(split):
将树以阈值
inline void split(int now,int val,int& x,int& y){// now 当前节点,val 是阈值,x 是 <=val 的树,y 是 >val 的树(都取根节点来表示) if(!now)x=y=0;//空树 else{ if(fhq[now].val<=val){// 假如当前节点的值小于等于阈值,则左子树中的所有节点都是 x 树 的,右子树则需要递归判断 x=now; split(fhq[now].rs,val,fhq[now].rs,y); } else{// 否则此时的右子树都属于 y 树,左子树递归判断 y=now; split(fhq[now].ls,val,x,fhq[now].ls); } update(now);// 记得更新节点信息 } }
合并(merge):
合并两棵树为一棵树,而且前面的树一定要是在左边的。
inline int merge(int x,int y){// 返回根节点编号 if(!x||!y)return x+y;// 假如两树有一树为空或是两树都为空,则返回其中不空的一棵或是返回空 if(fhq[x].key>fhq[y].key){// 假如 x 树根节点的键值大于 y 树根节点的,此时 x 树根节点一定在 y 树根节点上方。 //也就是 x 树根节点就是合并后两棵树的根节点,所以此时让右子树再去跟 y 树合并。 fhq[x].rs=merge(fhq[x].rs,y); update(x);// 更新节点信息 return x; } else{// 否则同理 fhq[y].ls=merge(x,fhq[y].ls); update(y); return y; } }
更新操作:
插入(insert):
插入一个值为
int x,y,z; inline void ins(int val){ split(root,val,x,y);//先将原树以 val 为阈值割分成两部分 x,y root=merge(merge(x,newnode(val)),y);//此时我们按 x,new,y 的顺序合并即可 }
删除(delete):
删除一个值为
inline void del(int val){ split(root,val,x,z);// 先将原树树割分成 <=val(x) 和 >val(z) split(x,val-1,x,y);// 再将 x 树割分成 <val(x) 和 =val(y) y=merge(fhq[y].ls,fhq[y].rs);//将 y 树中的根节点删除 root=merge(merge(x,y),z);// 合并回来 }
查询一个数的排名(getrank):
查询一个数
inline int getrank(int val){ split(root,val-1,x,y);//把 val-1 的部分割出来 int rank=fhq[x].siz+1;// 此时直接求就好 root=merge(x,y);// 合并回来 return rank; }
查询排名所对应的值(getnum):
查询排名
inline int getnum(int rank){ int now=root; while(now){ if(fhq[fhq[now].ls].siz+1==rank)break;// 假如这个节点就是,直接 break 掉 if(fhq[fhq[now].ls].siz<rank){// 左子树的节点不够 rank-=(fhq[fhq[now].ls].siz+1);//减掉左子树的节点数量,即为需要在右子树中找第几大的数 now=fhq[now].rs;// 往右走 } else now=fhq[now].ls;// 左子树节点数量还绰绰有余时,就往左子树走 } return fhq[now].val; }
查询一个数的前驱(pre):
查询在比值
inline int pre(int val){ split(root,val-1,x,y);// 把树割成 <val(x) 和 >=val(y) 两树 int now=x; while(fhq[now].rs)now=fhq[now].rs;// 在 x 树中最大的数就是最右边的数 root=merge(x,y);// 合并回来 return fhq[now].val; }
查询一个数的后继(nxt):
查询在比值
inline int nxt(int val){// 同理 split(root,val,x,y); int now=y; while(fhq[now].ls)now=fhq[now].ls; root=merge(x,y); return fhq[now].val; }
至此我们顺利的完成了所有操作。
code:
#include<bits/stdc++.h> #define int long long using namespace std; const int N=1e5+10; std::mt19937 rnd(114514); struct FHQ_Treap{ struct node{ int ls,rs; int val,key; int siz; }fhq[N]; int root,cnt; inline int newnode(int val){ fhq[++cnt].val=val; fhq[cnt].key=rnd(); fhq[cnt].siz=1; return cnt; } inline void update(int o){ fhq[o].siz=fhq[fhq[o].ls].siz+fhq[fhq[o].rs].siz+1; } inline void split(int now,int val,int& x,int& y){ if(!now)x=y=0; else{ if(fhq[now].val<=val){ x=now; split(fhq[now].rs,val,fhq[now].rs,y); } else{ y=now; split(fhq[now].ls,val,x,fhq[now].ls); } update(now); } } inline int merge(int x,int y){ if(!x||!y)return x+y; if(fhq[x].key>fhq[y].key){ fhq[x].rs=merge(fhq[x].rs,y); update(x); return x; } else{ fhq[y].ls=merge(x,fhq[y].ls); update(y); return y; } } int x,y,z; inline void ins(int val){ split(root,val,x,y); root=merge(merge(x,newnode(val)),y); } inline void del(int val){ split(root,val,x,z); split(x,val-1,x,y); y=merge(fhq[y].ls,fhq[y].rs); root=merge(merge(x,y),z); } inline int getrank(int val){ split(root,val-1,x,y); int rank=fhq[x].siz+1; root=merge(x,y); return rank; } inline int getnum(int rank){ int now=root; while(now){ if(fhq[fhq[now].ls].siz+1==rank)break; if(fhq[fhq[now].ls].siz<rank){ rank-=(fhq[fhq[now].ls].siz+1); now=fhq[now].rs; } else now=fhq[now].ls; } return fhq[now].val; } inline int pre(int val){ split(root,val-1,x,y); int now=x; while(fhq[now].rs)now=fhq[now].rs; root=merge(x,y); return fhq[now].val; } inline int nxt(int val){ split(root,val,x,y); int now=y; while(fhq[now].ls)now=fhq[now].ls; root=merge(x,y); return fhq[now].val; } }tree; signed main(){ std::ios::sync_with_stdio(false); cin.tie(0);cout.tie(0); int T;cin>>T; while(T--){ int opt,val; cin>>opt>>val; if(opt==1)tree.ins(val); if(opt==2)tree.del(val); if(opt==3)cout<<tree.getrank(val)<<"\n"; if(opt==4)cout<<tree.getnum(val)<<"\n"; if(opt==5)cout<<tree.pre(val)<<"\n"; if(opt==6)cout<<tree.nxt(val)<<"\n"; } return 0; }
本文作者:Little_corn
本文链接:https://www.cnblogs.com/little-corn/p/18157469
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步