Treap 学习笔记
前置知识:二叉搜索树(BST)
众所周知,二叉搜索树的会被数据卡,所以我们要对它进行一定的操作,让它平衡一点。
Treap是通过旋转+随机化的方法来进行该操作的,所以不会被卡,但是功能少,只能做最基础的操作,不能区间反转。
算法实现
众所周知,Treap=Tree(树)+Heap(堆)
struct JTZ{ int val,siz,cnt,ls,rs,f; }a[maxn];
我们在每个节点中相对于二叉搜索树中加一个信息: ,这是一个随机数。
我们需要保证以下内容:
首先保证它具有二叉搜索树的性质(这不是废话)。
其次,我们的 变量必须满足堆的性质,就是父亲节点的 比它两个儿子的 都小。
如何维护它堆的性质?旋转即可。
算法核心
算法核心就是旋转,大致长这个样子:
Update 2021.3.9:图片有误,现已更新
其实就是两个操作:Zig和Zag
由于Zig和Zag都不会影响二叉搜索树的性质,所以无论怎么转答案都是对的,但是效率可能会改变
void zag(int &p){//right int k=a[p].ls; a[p].ls=a[k].rs; a[k].rs=p; p=k; up(a[p].rs); up(p); return; } void zig(int &p){//left int k=a[p].rs; a[p].rs=a[k].ls; a[k].ls=p; p=k; up(a[p].ls); up(p); return; }
接下来是删除操作,只要把该节点旋转到叶子节点就可以了。
记得在插入时维护序列。
代码
#include<ctime> #include<cstdio> #include<cstdlib> #define maxn 100039 #define INF 0x3fffffff inline int max(int x,int y){ return x>y?x:y; } inline int min(int x,int y){ return x<y?x:y; } using namespace std; //#define debug typedef int Type; inline Type read(){ Type sum=0; int flag=0; char c=getchar(); while((c<'0'||c>'9')&&c!='-') c=getchar(); if(c=='-') c=getchar(),flag=1; while('0'<=c&&c<='9'){ sum=(sum<<1)+(sum<<3)+(c^48); c=getchar(); } if(flag) return -sum; return sum; } struct JTZ{ int val,siz,cnt,ls,rs,f; }a[maxn]; int root,n; int T; int op,x; void up(int rt){ a[rt].siz=a[rt].cnt+a[a[rt].ls].siz+a[a[rt].rs].siz; return; } int newnode(int x){ a[++n].val=x; a[n].cnt=1; a[n].f=rand(); return n; } void zag(int &p){//right int k=a[p].ls; a[p].ls=a[k].rs; a[k].rs=p; p=k; up(a[p].rs); up(p); return; } void zig(int &p){//left int k=a[p].rs; a[p].rs=a[k].ls; a[k].ls=p; p=k; up(a[p].ls); up(p); return; } void insert(int &rt,int x){ if(!rt) rt=newnode(x); else if(a[rt].val==x) a[rt].cnt++; else if(a[rt].val>x){ insert(a[rt].ls,x); if(a[rt].f>a[a[rt].ls].f) zag(rt); } else{ insert(a[rt].rs,x); if(a[rt].f>a[a[rt].rs].f) zig(rt); } up(rt); return; } void del(int &rt,int x){ if(a[rt].val==x){ if(a[rt].cnt>1) a[rt].cnt--; else{ if(!a[rt].ls) rt=a[rt].rs; else if(!a[rt].rs) rt=a[rt].ls; else if(a[a[rt].ls].f<a[a[rt].rs].f) zag(rt),del(a[rt].rs,x); else zig(rt),del(a[rt].ls,x); } } else if(a[rt].val<x) del(a[rt].rs,x); else del(a[rt].ls,x); up(rt); } int findrk(int rt,int x){ if(!rt) return 0; if(a[rt].val==x) return a[a[rt].ls].siz+1; else if(a[rt].val>x) return findrk(a[rt].ls,x); else return findrk(a[rt].rs,x)+a[rt].cnt+a[a[rt].ls].siz; } int findval(int rt,int x){ if(a[a[rt].ls].siz<x&&x<=a[a[rt].ls].siz+a[rt].cnt) return a[rt].val; else if(x<=a[a[rt].ls].siz) return findval(a[rt].ls,x); else return findval(a[rt].rs,x-a[a[rt].ls].siz-a[rt].cnt); } int findpre(int rt,int x){ if(!rt) return -INF; if(x>a[rt].val) return max(a[rt].val,findpre(a[rt].rs,x)); return findpre(a[rt].ls,x); } int findnex(int rt,int x){ if(!rt) return INF; if(x<a[rt].val) return min(a[rt].val,findnex(a[rt].ls,x)); return findnex(a[rt].rs,x); } int dfs(int rt){ if(!rt) return 0; return max(dfs(a[rt].ls),dfs(a[rt].rs))+1; } int main(){ //freopen("P3369_12.in","r",stdin); //freopen("1.out","w",stdout); T=read(); srand(time(0)); newnode(-INF); newnode(INF); a[1].f=-INF; a[2].f=INF; a[1].rs=2; up(1); up(2); root=1; int i=0; while(T--){ op=read(); x=read(); if(op==1) insert(root,x); if(op==2) del(root,x); if(op==3) printf("%d\n",findrk(root,x)-1); if(op==4) printf("%d\n",findval(root,x+1)); if(op==5) printf("%d\n",findpre(root,x)); if(op==6) printf("%d\n",findnex(root,x)); } //printf("%d",dfs(root)); return 0; }
关于效率
显然,树高越低效率越高。
有些时候平衡树写假了,当然答案肯定是对的,但是因为数据水所以我们可能会卡过去,所以我们首先骗到最后一个数据,然后运行一遍在检验树高(也就是 dfs
函数)就可以看看平衡树是否写假了,树高应该是略大于
分类:
笔记
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具