Splay 学习笔记
前言
咕咕咕咕咕
其实 Splay 是和 Treap 差不多时候学的,但是由于 某些原因 <- 模板没调出来 & 准备初赛没来得及写 所以一直咕咕咕到了现在。。。
那就步入正题吧。
前置知识:Treap的旋转
简要介绍
Splay 是一种平衡树,所以说显然它符合二叉搜索树(BST)的性质。
Splay 能干什么呢?它可以维护序列信息和区间信息。包括区间翻转,区间插入,区间删除以及一些区间操作,比如说区间加等等。
Splaying
Splay的核心操作就在于 splay (旋转),我们定义函数 splay(x,y)
代表把节点 x
变成节点 y
的儿子。
有了这个函数,在操作的时候我们只要把要操作的两个节点变成根节点和根节点的右儿子,这要我们只要对根节点的右儿子的左儿子为根的子树就可以了。如果是插入,就直接插进来,删除就只接删掉,否则就是打一个懒惰标记(类似于线段树的操作)就可以了。
接下来我们讲函数 splay(x,y)
该如何实现。
显然我们需要通过类似 Treap 的旋转来实现,我们只要把一个节点转上去就好了。但是我们发现,如果直接把这个节点向上转的话会影响平衡树的平衡,于是在 splay 的时候,我们每次需要两次 Zig/Zag 使该节点的深度减一,如果一个节点和它的父亲都是他们的父亲的左儿子或者是右儿子,那么就先转两次该节点,否则就先转这个节点的父亲再转这个节点 ,这样就可以保证这课树的平衡。大家可以手动模拟一下就可以体会为什么这样就可以让树更加平衡了。
时间复杂度显然是 。
代码:
struct JTZ{ int ch[2],p,v,siz,flag; // 分别代表:儿子,父亲,编号,字树的大小,懒惰标记(是否区间翻转) // ch[0]是左儿子,ch[1]是右儿子 }a[maxn]; void up(int p){ a[p].siz=a[a[p].ch[0]].siz+a[a[p].ch[1]].siz+1; return; } void down(int p){ if(a[p].flag){ swap(a[p].ch[0],a[p].ch[1]); a[a[p].ch[0]].flag^=1; a[a[p].ch[1]].flag^=1; } a[p].flag=0; return; }//下推标记 void rotate(int x){ int y=a[x].p,z=a[y].p,k=a[y].ch[1]==x; a[z].ch[a[z].ch[1]==y]=x,a[x].p=z; a[y].ch[k]=a[x].ch[k^1],a[a[x].ch[k^1]].p=y; a[x].ch[k^1]=y,a[y].p=x; up(y); up(x); return; }//rotate(x) 代表通过旋转让节点 x 的深度减一 void splay(int x,int k){ register int y,z; while(a[x].p!=k){ y=a[x].p; z=a[y].p; if(z!=k) if((a[y].ch[1]==x)!=(a[z].ch[1]==y)) rotate(x); else rotate(y); rotate(x); } if(!k) root=x; return; }
【推荐】国内首个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工具