前言
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
非常好用!