splay
前言
ZJOI倒計時,開始復(fù)習(xí)一些沒寫過博客也好久沒碼的算法啦
介紹
splay是一個平衡樹,他滿足二叉搜索樹的性質(zhì):左子樹的值都小于等于自己,右子樹的值都大于等于自己
splay的思想是通過rotate的方式把要操作的點(diǎn)轉(zhuǎn)到根節(jié)點(diǎn)
空間復(fù)雜度O(n)\mathcal O(n)O(n),時間復(fù)雜度單次均攤O(nlogn)\mathcal O(nlogn)O(nlogn)
單旋
然后一次rotate就只有左旋右旋兩種
這里貼一幅比較好理解的畫
想成下面3個點(diǎn)不動,左右旋只是上面兩個點(diǎn)的一個變化,就很好記憶了
另外ZIG=右旋,ZAG=左旋
左側(cè)的ZIG(藍(lán)點(diǎn))為圖示左旋
右側(cè)的ZAG(紫點(diǎn))為圖示右旋
雙旋
ZIG或ZAG
我們發(fā)現(xiàn),如果當(dāng)前節(jié)點(diǎn)的父親就是根節(jié)點(diǎn),那么直接進(jìn)行一次ZIG或者ZAG即可
ZIG-ZIG或ZAG-ZAG
如果父親節(jié)點(diǎn)不是根節(jié)點(diǎn),那么:
滿足自己是父親節(jié)點(diǎn)的左兒子且父親節(jié)點(diǎn)是祖父節(jié)點(diǎn)的左兒子,那么執(zhí)行ZIG(父親節(jié)點(diǎn))-ZIG(自己)即可
滿足自己是父親節(jié)點(diǎn)的右兒子且父親節(jié)點(diǎn)是祖父節(jié)點(diǎn)的右兒子,那么執(zhí)行ZAG(父親節(jié)點(diǎn))-ZAG(自己)即可
如圖所示,從左到右是ZIG-ZIG,從右到左是ZAG-ZAG
(這幅圖里pic2藍(lán)點(diǎn)和褐點(diǎn)高低不同是為了第一步觀看方便,其實(shí)沒啥用)
其實(shí)非常清新
ZIG-ZAG或ZAG-ZIG
如果父親節(jié)點(diǎn)不是根節(jié)點(diǎn),那么:
滿足自己是父親節(jié)點(diǎn)的左兒子且父親節(jié)點(diǎn)是祖父節(jié)點(diǎn)的右兒子,那么執(zhí)行ZIG(自己)-ZAG(自己)即可
滿足自己是父親節(jié)點(diǎn)的右兒子且父親節(jié)點(diǎn)是祖父節(jié)點(diǎn)的左兒子,那么執(zhí)行ZAG(自己)-ZIG(自己)即可
給出ZIG-ZAG的圖
至于ZAG-ZIG的圖就請你自行腦補(bǔ)吧
至此,我們學(xué)會了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; }總結(jié)
splay雖然速度在平衡樹中不算很快,但是其非常靈活,可以支持一些區(qū)間操作
貼一道比較常見的模板題
[luogu3391]【模板】文藝平衡樹
另外,利用splay還可以做Link-Cut Trees
非常好用!
總結(jié)
- 上一篇: [loj556][Antileaf's
- 下一篇: 模拟赛-20190228-随机数(ran