splay

2 篇文章 0 订阅
1 篇文章 0 订阅

前言

ZJOI倒计时,开始复习一些没写过博客也好久没码的算法啦

介绍

splay是一个平衡树,他满足二叉搜索树的性质:左子树的值都小于等于自己,右子树的值都大于等于自己
splay的思想是通过rotate的方式把要操作的点转到根节点
空间复杂度 O ( n ) \mathcal O(n) O(n),时间复杂度单次均摊 O ( n l o g n ) \mathcal O(nlogn) O(nlogn)

单旋

然后一次rotate就只有左旋右旋两种
这里贴一幅比较好理解的画

在这里插入图片描述
想成下面3个点不动,左右旋只是上面两个点的一个变化,就很好记忆了
另外ZIG=右旋,ZAG=左旋
左侧的ZIG(蓝点)为图示左旋
右侧的ZAG(紫点)为图示右旋

双旋

ZIG或ZAG

我们发现,如果当前节点的父亲就是根节点,那么直接进行一次ZIG或者ZAG即可

ZIG-ZIG或ZAG-ZAG

如果父亲节点不是根节点,那么:
满足自己是父亲节点的左儿子且父亲节点是祖父节点的左儿子,那么执行ZIG(父亲节点)-ZIG(自己)即可
满足自己是父亲节点的右儿子且父亲节点是祖父节点的右儿子,那么执行ZAG(父亲节点)-ZAG(自己)即可
在这里插入图片描述
如图所示,从左到右是ZIG-ZIG,从右到左是ZAG-ZAG
(这幅图里pic2蓝点和褐点高低不同是为了第一步观看方便,其实没啥用
其实非常清新

ZIG-ZAG或ZAG-ZIG

如果父亲节点不是根节点,那么:
满足自己是父亲节点的左儿子且父亲节点是祖父节点的右儿子,那么执行ZIG(自己)-ZAG(自己)即可
满足自己是父亲节点的右儿子且父亲节点是祖父节点的左儿子,那么执行ZAG(自己)-ZIG(自己)即可
在这里插入图片描述
给出ZIG-ZAG的图
至于ZAG-ZIG的图就请你自行脑补吧
至此,我们学会了splay的Splay操作

其它操作

有了splay操作后,其它操作就比较方便了,直接看代码即可
给出模板题
[luogu3369]【模板】普通平衡树

代码

贴个指针版的代码(指针大法好!

#include<cstdio>
#include<cctype>
#include<algorithm>
namespace fast_IO
{
	const int IN_LEN=10000000,OUT_LEN=10000000;
	char ibuf[IN_LEN],obuf[OUT_LEN],*ih=ibuf+IN_LEN,*oh=obuf,*lastin=ibuf+IN_LEN,*lastout=obuf+OUT_LEN-1;
	inline char getchar_(){return (ih==lastin)&&(lastin=(ih=ibuf)+fread(ibuf,1,IN_LEN,stdin),ih==lastin)?EOF:*ih++;}
	inline void putchar_(const char x){if(oh==lastout)fwrite(obuf,1,oh-obuf,stdout),oh=obuf;*oh++=x;}
	inline void flush(){fwrite(obuf,1,oh-obuf,stdout);}
}
using namespace fast_IO;
#define getchar() getchar_()
#define putchar(x) putchar_((x))
#define rg register
typedef long long ll;
template <typename T> inline T max(const T a,const T b){return a>b?a:b;}
template <typename T> inline T min(const T a,const T b){return a<b?a:b;}
template <typename T> inline void mind(T&a,const T b){a=a<b?a:b;}
template <typename T> inline void maxd(T&a,const T b){a=a>b?a:b;}
template <typename T> inline T abs(const T a){return a>0?a:-a;}
template <typename T> inline void swap(T&a,T&b){T c=a;a=b;b=c;}
template <typename T> inline T gcd(const T a,const T b){if(!b)return a;return gcd(b,a%b);}
template <typename T> inline T lcm(const T a,const T b){return a/gcd(a,b)*b;}
template <typename T> inline T square(const T x){return x*x;};
template <typename T> inline void read(T&x)
{
	char cu=getchar();x=0;bool fla=0;
	while(!isdigit(cu)){if(cu=='-')fla=1;cu=getchar();}
	while(isdigit(cu))x=x*10+cu-'0',cu=getchar();
	if(fla)x=-x;
}
template <typename T> inline void printe(const T x)
{
	if(x>=10)printe(x/10);
	putchar(x%10+'0');
}
template <typename T> inline void print(const T x)
{
	if(x<0)putchar('-'),printe(-x);
	else printe(x);
}
const int maxn=101000;
struct node
{
    node*lson,*rson,*fa;
    int size,val;
    inline void update(){size=lson->size+rson->size+1;}
}Q[maxn],empty,*Null=&empty,*root=Null;
int tot;
inline void clear(node*x,const int val,node*FA)
{
    x->size=1,x->val=val,x->lson=x->rson=Null,x->fa=FA;
}
inline node*newnode(){return &Q[++tot];}
inline void delnode(node* x){}
inline void rotate(node*x)
{
    node*y=x->fa;
    node*z=y->fa;
    bool k=y->lson==x;
    if(z!=Null)(z->lson==y?z->lson:z->rson)=x;
    x->fa=z;
    (k?y->lson:y->rson)=(k?x->rson:x->lson);
    (k?x->rson:x->lson)->fa=y;
    (k?x->rson:x->lson)=y;
    y->fa=x;
    x->update(),y->update();
}
inline void Splay(node*x,const node*wan)
{
    while(x->fa!=wan)
    {
        node*y=x->fa;node*z=y->fa;
        if(z!=wan)(z->rson==y)^(y->rson==x)?rotate(x):rotate(y);
        rotate(x);
    }
    if(wan==Null)root=x;
}
inline void insert(const int x)
{
    node*u=root,*fa=Null;
    while(u!=Null)fa=u,u=x<=u->val?u->lson:u->rson;
    u=newnode();
    if(fa!=Null)(x<=fa->val?fa->lson:fa->rson)=u;
    clear(u,x,fa);
    Splay(u,Null);
}
inline node *find(const int x)
{
    node*u=root,*ans=Null;
    while(u!=Null)u=u->val<x?u->rson:(ans=u,u->lson);
    return ans;
}
inline node*prec(const int x)
{
    node*u=root,*ans=Null;
    while(u!=Null)u=u->val<x?ans=u,u->rson:u->lson;
    return ans;
}
inline node*succ(const int x)
{
    node*u=root,*ans=Null;
    while(u!=Null)u=u->val>x?ans=u,u->lson:u->rson;
    return ans;
}
inline node*Kth(rg int x)
{
    node*u=root;
    while(1)
    {
        if(u->lson->size>=x)u=u->lson;
        else if(u->lson->size+1==x)return u;
        else x-=u->lson->size+1,u=u->rson;
    }
}
int n;
int main()
{
//	freopen("splay.in","r",stdin);freopen("splay.out","w",stdout);
    empty.fa=empty.lson=empty.rson=Null,empty.size=0,empty.val=-10000001;
    read(n);
    for(rg int i=1;i<=n;i++)
    {
     	int opt,x;read(opt),read(x);
 		if(opt==1)insert(x);
 		else if(opt==2)
        {
            node *u=prec(x),*v=find(x);
            if(u==Null)
            {
                Splay(v,Null);
                root=root->rson;
                root->fa=Null;
            }
            else
            {
                Splay(u,Null);
                Splay(v,u);
                u->rson=v->rson;
                v->rson->fa=u;
            }
            delnode(v);
        }
        else if(opt==3)
        {
            node *u=find(x);
            Splay(u,Null);
            print(u->lson->size+1),putchar('\n');
        }
        else if(opt==4)
        {
            node *u=Kth(x);
            Splay(u,Null);
            print(u->val),putchar('\n'); 
        }
        else if(opt==5)
        {
            node *u=prec(x);
            Splay(u,Null);
            print(u->val),putchar('\n');
        }
        else if(opt==6)
        {
            node *u=succ(x);
            Splay(u,Null);
            print(u->val),putchar('\n');
        }
    }
    return flush(),0; 
}
总结

splay虽然速度在平衡树中不算很快,但是其非常灵活,可以支持一些区间操作
贴一道比较常见的模板题
[luogu3391]【模板】文艺平衡树
另外,利用splay还可以做Link-Cut Trees
非常好用!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值