Splay树模板
P3369 [模板]普通平衡树
#include<bits/stdc++.h>
using namespace std;
#define ls(x) tr[x].s[0]
#define rs(x) tr[x].s[1]
const int maxn=1e5+10;
//node
struct node{
int s[2],val,cnt,siz,fa;
//左右儿子,value,出现的次数,子树大小,父节点编号
void init(int p,int v){
fa=p;val=v;
cnt=siz=1;
}
}tr[maxn];
int root,idx=0;//根节点编号,总的节点数量
//pushup更新每个节点的size
void pushup(int x){
tr[x].siz=tr[ls(x)].siz+tr[rs(x)].siz+tr[x].cnt;
//siz是该节点子树的节点数量,不要忘记+cnt
}
//rotate旋转
void rotate(int x){
int y=tr[x].fa,z=tr[y].fa;
int k= rs(y)==x;
tr[y].s[k]=tr[x].s[k^1];
tr[tr[x].s[k^1]].fa=y;//更新y->b
tr[x].s[k^1]=y;
tr[y].fa=x;//更新边x->y
tr[z].s[rs(z)==y]=x;
tr[x].fa=z;//更新边z->x
pushup(y);pushup(x);//顺序必须是先更新y再更新x,因为y是x的儿子
}
//splay把x旋转到k节点下面
void splay(int x,int k){
while(tr[x].fa!=k){//只要上一位不是k就一直执行
int y=tr[x].fa,z=tr[y].fa;
if(z!=k) //折转底,直转中
(rs(y)==x)^(rs(z)==y)
?rotate(x):rotate(y);
rotate(x);
}
if(k==0) root=x;//标记根节点
}
//find查找val并把它转到根节点
void find(int val){
int x=root;
while(tr[x].s[val>tr[x].val]&&val!=tr[x].val)
x=tr[x].s[val>tr[x].val];
splay(x,0);
}
//get_pre得到val的前驱
//返回的是节点的标号
int get_pre(int val){
find(val);
int x=root;
if(val>tr[x].val) return x;
x=ls(x);
while(tr[x].s[1]) x=rs(x);
return x;
}
//get_suc得到val的后继
//返回节点编号而不是值
int get_suc(int val){
find(val);
int x=root;
if(val<tr[x].val) return x;
x=rs(x);
while(tr[x].s[0]) x=ls(x);
return x;
}
//update插入数字val
void update(int val){
int x=root,p=0;
while(x&&val!=tr[x].val)//只要是这一个点有值就行,不用判断儿子有值
p=x,x=tr[x].s[val>tr[x].val];
if(!x){
x=++idx;
tr[p].s[val>tr[p].val]=x;//在父节点上加一个儿子
tr[x].init(p,val);
}
else tr[x].cnt++;
splay(x,0);//更新整棵树
}
//get_rank找到val的排名
int get_rank(int val){
find(val);
return tr[tr[root].s[0]].siz;
}
//find_rank找到排名为k的数
int find_rank(int k){
int x=root;
while(1){
if(tr[tr[x].s[0]].siz+tr[x].cnt<k){
k-=(tr[tr[x].s[0]].siz+tr[x].cnt);
x=rs(x);//这个时候x已经改变了,所以k应该在前面就减了
}
else{
if(tr[tr[x].s[0]].siz>=k) x=ls(x);
else break;
}
}
splay(x,0);
return tr[x].val;
}
//del删除数字val
void del(int val){
int y=get_pre(val),z=get_suc(val);
splay(y,0),splay(z,y);
int x=tr[z].s[0];
if(tr[x].cnt>1) tr[x].cnt--,splay(x,0);
else tr[z].s[0]=0,splay(z,0);
}
int main(){
/*2023.4.15 hewanying P3369 [模板]普通平衡树 Splay树*/
int n;
update(-1e9);update(1e9);//哨兵
scanf("%d",&n);
int opt=0,x;
while(n--){
scanf("%d%d",&opt,&x);
if(opt==1) update(x);
if(opt==2) del(x);
if(opt==3) printf("%d\n",get_rank(x));
if(opt==4) printf("%d\n",find_rank(x+1));
if(opt==5) printf("%d\n",tr[get_pre(x)].val);
if(opt==6) printf("%d\n",tr[get_suc(x)].val);
}
return 0;
}
每一次rotate
有顺时针旋转和逆时针旋转两种情况
每一次splay
折转底,直转中
在一次旋转后,这棵二叉查找树就变得矮胖了
splay作用
1.把想要的节点转到根节点
2.更新整棵树,所以每一次修改过后都要splay一次
时间复杂度
\(O(m\log_{2}{n})\)
标签:splay,val,get,int,tr,Splay,节点,模板 From: https://www.cnblogs.com/hewanying0622/p/17320686.html