DS笔记
数据结构都快忘了,再学点。
以上的不会再写,只会再写别的。
DS都快忘了。
分块学习笔记不会再写,只会再写别的。
平衡树
众所周知,BST很方便,就是会退化成链或树很高,所以我们需要更高效的DS,那就是——平衡树。
Splay
Splay好闪,拜谢Splay。
Splay是通过不断将某个节点旋转至根来保持平衡,同时满足BST的性质。
怎么旋转呢?我们来画两张图!
左旋
这是一张图:
经过左旋后变为:
可以看出就是将
右旋
和左旋相反,下边给出图示(其实就是图片倒了一下):
经过右旋后变为:
同样可以看出,就是将
其实就是相反的。
某个节点的旋转就是这样的:
-
如果他是某个节点的左儿子,就右旋。
-
如果他是某个节点的右儿子,就左旋。
但是旋一次只会和父亲交换而不是到根,咋整?
答案是暴力。
是的,你没看错,只需要暴力转就可以了。
那不就写完了?溜了溜了。
别急!这是单旋Splay,它在棺材里躺得好好的呢!
单旋Splay很容易被卡到链。
如下图,如果有三点共线的情况,那么最后跳来跳去还是一条链,那咋办?
先转
然后我们也可以得出规律了,就是先转父亲,再转自己一直这样直到自己变成根。
下面借一下大佬Enoch006的图,就是这样修改的:
接下来讲代码和操作啦。
先来声明一下变量捏。
siz//代表整棵Splay的大小
root//Splay的根节点
sz[i]//i的子树的大小
num[i]//i这个节点的值
cnt[i]//i这个节点的值出现的次数
fa[i]//i的父亲
son[i][0]//i的左儿子
son[i][1]//i的右儿子
清空
这个操作在删除后执行。
void all_zero(int x){
fa[x]=0;
son[x][0]=0;
son[x][1]=0;
num[x]=0;
cnt[x]=0;
sz[x]=0;
}
get
判断当前的点是左儿子还是右儿子(旋转要用)。
int get(int x){
if(x==son[fa[x]][0]){
return 0;
}return 1;
}
pushup
随便写的名字
用于修改后确认并修改树的大小。
void pushup(int x){
if(x>=1){
sz[x]=cnt[x];
if(son[x][0]>=1){
sz[x]+=sz[son[x][0]];
}if(son[x][1]>=1){
sz[x]+=sz[son[x][1]];
}
}
}
rotate
如下图。
我们要旋转
一开始
设 flag
为 get(4)
,那么就是
那么可以发现,flag
儿子和 flag^1
儿子还有
其实旋转就是连边、断边。
(下面的数字就是上面那棵树的节点)。
于是我们先把
那么旋转就好了。
void rotate(int x){
int f=fa[x],flag=get(x);
int gra=fa[f];
son[f][flag]=son[x][flag^1];
fa[son[f][flag]]=f;
son[x][flag^1]=f;
fa[f]=x,fa[x]=gra;
if(gra>=1){
if(son[gra][1]==f){
son[gra][1]=x
}else{
son[gra][0]=x
}
}pushup(f),pushup(x);
}
Splay
双旋的关键。
这里判断两种情况:
-
三点一线,这样就是上面说的,先转父亲,再转自己一直这样直到自己变成根。
-
不是三点一线,
疯狂暴力旋转即可。
void Splay(int x){
for(int f=0,f=fa[x];rotate(x)){
if(fa[f]>=1){
if(get(x)==get(fa)){
rotate(fa);
}else{
rotate(x);
}
}
}rt=x;
}
insert
也是分类讨论:
-
如果
rt==0
,那么树肯定是空的,那么
咕了。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构