既然选择了远方,便只顾风雨兼行|

H_W_Y

园龄:1年11个月粉丝:28关注:15

2023-04-15 10:52阅读: 15评论: 0推荐: 0

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(mlog2n)

本文作者:H_W_Y

本文链接:https://www.cnblogs.com/H-W-Y/p/17320686.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   H_W_Y  阅读(15)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起