Splay树【模板】

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

有顺时针旋转和逆时针旋转两种情况
image

每一次splay

折转底,直转中
在一次旋转后,这棵二叉查找树就变得矮胖了

splay作用

1.把想要的节点转到根节点
2.更新整棵树,所以每一次修改过后都要splay一次

时间复杂度

O ( m log ⁡ 2 n ) O(m\log_{2}{n}) O(mlog2n)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值