Splay平衡树板子

学习链接:Splay - 小蒟蒻laf - 博客园

模板题:【模板】普通平衡树 - 洛谷 

 暂时只抄了个板子,基本理解了代码,内容还有待补充。

#include<bits/stdc++.h>
using namespace std;
const int N=100005,INF=2147483647;
int cnt[N],val[N],sz[N],fa[N],ch[N][2],root,tot;
// ! ch[x][0]:left
// ! ch[x][1]:right
inline int Get(int x) { return ch[fa[x]][1]==x; } //该点是否为父亲的右孩子
inline void Up(int x) { sz[x]=cnt[x]+sz[ch[x][0]]+sz[ch[x][1]]; } //维护sz(sz[u]表示子树大小)
inline void Rot(int x) { //对x结点进行一次单旋转
	register int y=fa[x],z=fa[y],k=Get(x);
	ch[z][Get(y)]=x,fa[x]=z;
	ch[y][k]=ch[x][k^1],fa[ch[x][k^1]]=y;
	ch[x][k^1]=y,fa[y]=x;
	Up(y),Up(x);
}
inline void Splay(int x,int goal=0) { //把x结点旋转到goal的下面(默认情况下goal是0,也就是让x为根)
	for(int y;(y=fa[x])^goal;Rot(x)) //不断rot,直到x在goal的下面
		if(fa[y]^goal)Rot(Get(x)^Get(y)?x:y); //但这里要注意,如果链比较长,那就先旋转父节点,再旋转当前结点
	if(!goal)root=x; //goal=0的情况下,x作为根
}
inline void Find(int x) { //寻找值最接近x的结点,并且让这个点变成根
	if(!root)return;
	register int u=root; //要求寻找root的子树内的结果,所以从u=root开始
	while(ch[u][x>val[u]] && x^val[u]) //还没找到x,往下走
		u=ch[u][x>val[u]];
	Splay(u);
}
inline void Ins(int x) { //插入x,并且让x所在点变成根
	register int u=root,fu=0; //当前结点,父节点
	while(u && val[u]^x)fu=u,u=ch[u][x>val[u]]; //往下走
	if(u)cnt[u]++; //出现过,那就只需要cnt++
	else { //没出现过,新建结点
		u=++tot;
		if(fu)ch[fu][x>val[fu]]=u; //父亲进行child连接
		ch[u][0]=ch[u][1]=0; //新结点没有儿子
		fa[u]=fu,val[u]=x; //更新fa和val
		cnt[u]=sz[u]=1;    //出现次数和子树大小,初始是1
	}
	Splay(u);
}
inline int Nxt(int x,int f) {
	Find(x); //寻找x所在结点编号,记录在root处
	register int u=root; //让u等于root,即x所在结点编号
	if(val[u]>x && f)return u; //最接近x的数比x大,我们恰好要找最小的x大的数,直接返回这个结点编号
	if(val[u]<x &&!f)return u; //同上
	u=ch[u][f]; //不满足,下面两行就是继续寻找另一个方向最接近的(自行理解一下)
	while(ch[u][f^1])u=ch[u][f^1];
	return u;
}
inline void Del(int x) { //删除一个x
	register int lst=Nxt(x,0),nxt=Nxt(x,1); //前驱和后继
	Splay(lst),Splay(nxt,lst); //让lst做根,nxt做lst的右儿子,这样就一定能使得nxt的左儿子是x!
	register int del=ch[nxt][0]; //del一定就是x所在结点
	if(cnt[del]>1)--cnt[del],Splay(del); //如果删完还有,要继续Splay一下
	else ch[nxt][0]=0; //如果删完没有了,那就置空nxt的左指针
}
inline int Kth(int k) { //输出第k大的数
	register int u=root,sn=0; //当前结点,左儿子结点
	for(;;) {
		sn=ch[u][0];
		if(k>sz[sn]+cnt[u])
			k-=sz[sn]+cnt[u],u=ch[u][1];
		else if(sz[sn]>=k)u=sn;
		else return val[u];
	}
}
int T;
int main() {
	Ins(-INF); //加入两个哨兵结点,防止越界的情况出现
	Ins(+INF);
	scanf("%d",&T);
	for(int opt,x;T--;) {
		scanf("%d%d",&opt,&x);
		if(opt==1)Ins(x); //插入x
		else if(opt==2)Del(x); //删除一个x
		else if(opt==3)Find(x),printf("%d\n",sz[ch[root][0]]); //让x所在结点作为根,那么根的左子树大小+1就是排名,但是由于有-INF,又要-1,综合起来就是不用加也不用减
		else if(opt==4)printf("%d\n",Kth(x+1)); //输出排名第k+1的数(因为有个-INF的影响,所以要+1)
		else if(opt==5)printf("%d\n",val[Nxt(x,0)]); //x的前驱
		else if(opt==6)printf("%d\n",val[Nxt(x,1)]); //x的后继
	}
} 

to be continued...

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值