【算法】 splay

感谢yyb大佬的博客!

splay,初看像是个十分羞耻高逼格的名字,splay也确实是一个神级的数据结构,虽然同机房大佬rayment不停推荐treap,但我还是决定先写一波splay

splay不得不介绍的,便是它的旋转(rotate)操作了,双旋splay可以毫无压力的将二叉查找树保持平衡,从而轻易使查找等操作保持在O(logn)的复杂度。

stl大法好!有像set、rope这样的东西,我要它有何用?但手打毕竟还是更加好的,很多操作都可以轻松实现。

such as 求出区间和/滑稽(这特么不是线段树该干的事吗?)

下面我们简单展示一下rotate操作,像splay操作、delete操作的话有些小复杂,蒟蒻不好描述,泥萌可以找到一些好博客看一看QWQ(留坑)

这就是旋转过程啦!虽然并没有使树的深度变短,但却skr很好的例子。

图中,你们可以发现两张图的先序遍历是没有区别de。此图是将a左转了一下。y就需要跑到a的右子书去,但喜闻乐见的是,a的右子树还挺抢手。那么,我们就可以将d丢到y的左子树上,再把y-b扯出来,放到a的右子树

似不似很简单!

下面是模板题普通平衡树的代码

#include <bits/stdc++.h>
#define maxn 100010
using namespace std;
int ch[maxn][2],f[maxn],key[maxn],cnt[maxn],size[maxn];//我们选择直接把splay放外面,ps.cnt表示该值出现了几次,而size表示以i为根的子树大小
int tot=0,root;//tot表示树的大小,root是树的根
inline int get(int x){
	if (ch[f[x]][0]==x)return 0;
	else return 1;//这里是看x是左子树还是右子树
}
inline void pushup(int u){
    size[u]=size[ch[u][0]]+size[ch[u][1]]+cnt[u];
}//更新u的size与cnt
inline void rotate(int x){
	int fa=f[x],old=f[fa];
	int w=get(x);//它是左子树,还是右子树?
	ch[old][ch[old][1]==fa]=x;//爷爷的儿子变孙子
	f[x]=old;//爸爸变爷爷
	ch[fa][w]=ch[x][w^1];//儿子的儿子变爸爸的儿子
	f[ch[x][w^1]]=fa;//儿子的儿子的爸爸变爸爸的爸爸
	ch[x][w^1]=fa;//儿子的爸爸变儿子
	f[fa]=x;//爸爸的儿子变爸爸
	pushup(fa);pushup(x);、、更新
}
inline void splay(int x,int goal){
	for (;f[x]!=goal;rotate(x)){
		int fa=f[x];
		if (f[fa]!=goal)
		rotate((get(x)==get(fa))?fa:x);
	}
	if (goal==0) root=x;//双旋splay
}
inline void insert(int v){
	int x=root,fa=0;
	while (x&&key[x]!=v){
		fa=x;
		x=ch[x][v>key[x]];
	}
	if (x) cnt[x]++;//如果x已经存在,那么cnt[x]++;
	else {
		tot++;
		x=tot;
		if (fa)
			ch[fa][v>key[fa]]=x;
		ch[x][0]=ch[x][1]=0;
		f[tot]=fa;
		key[tot]=v;
		cnt[tot]=size[tot]=1;//记得要将cnt和size更新!
	}
	splay(x,0);
}//二叉查找树的标准插♂入,只是记得要将插入点splay到根来保持平衡
inline void find(int x){
	int r=root;
	if (r==0)return ;
	while(ch[r][x>key[r]]&&x!=key[r])
        r=ch[r][x>key[r]];
    splay(x,0);
}
inline int next(int x){
	find(x);
	int r=root;
	if(key[r]>x)return r;
    r=ch[r][1];
    while(ch[r][1])r=ch[r][1];
    return r;
}
inline int pre(int x){
	find(x);
	int r=root;
	if(key[r]<x)return r;
    r=ch[r][0];
    while(ch[r][0])r=ch[r][0];
    return r;	
}
inline void del(int x){
	int up=next(x);
	int down=pre(x);
	splay(up,0);splay(down,up);
	int u=ch[down][0];
	if (cnt[u]>1){
		cnt[u]--;
		splay(u,0);
	}
	else ch[down][0]=0;
}
inline int kth(int x)
{
    int u=root;
	if(size[u]<x)
        return 0;
    while(1)
    {
        int y=ch[u][0];
        if(x>size[y]+cnt[u])
        {
            x-=size[y]+cnt[u];
            u=ch[u][1];
        }
        else
            if(size[y]>=x)
                u=y;
            else
                return key[u];
    }
} //yyb大佬的kth与del,我还太弱只能照着写
int main(){
	int i,j,n;
	cin>>n;
	insert(2147483647);
	insert(-2147483647);
	for (i=1;i<=n;i++){
		int x,y;
		cin>>x>>y;
		if (x==1)
			insert(y);
		if (x==2)
			del(y);
		if (x==3)
			 printf("%d\n",size[ch[root][0]]);
		if (x==4)
			cout<<kth(x+1)<<endl;
	    if (x==5)
	    	cout<<key[pre(y)]<<endl;
	    if (x==6)
	    	cout<<key[next(y)]<<endl;
	}
	return 0;
}

此代码还未调试结束qwq,所以是错的。。

猜你喜欢

转载自blog.csdn.net/qq_39441542/article/details/84675029