1. 程式人生 > >Splay算法摘要

Splay算法摘要

code res 分享圖片 turn ima get 重置 大小 旋轉

先介紹變量定義

技術分享圖片
 1 int n;
 2 struct Node {                                                                                        //Splay節點定義 
 3     int fa,son[2],val,num,siz;                                                                        //fa:它爸爸;son它兒子,左0右1;val:這個節點的值
 4                                                                                                     //
num:這個值的數量;siz:以它為根的子樹的大小 5 void res() { //重置節點,用於刪除 6 fa=son[0]=son[1]=val=num=siz=0; 7 } 8 } tree[N]; 9 int ins; 10 int root; 11 int mem[N],inm; //
內存回收池(其實並沒有什麽用)
var

判斷一個節點是它爸爸的左兒子還是右兒子

這個很簡單,比較一下它的值和它爸爸的值就行了

技術分享圖片
1 char ison(int x) {                                                                                    //快速判斷一個節點是它爸爸的左兒子(0)還是右兒子 (1) 
2     return x==tree[tree[x].fa].son[1];
3 }
ison

Splay的旋轉

旋轉分為兩種主要情況,一種是操作目標的爸爸是根,一種是操作目標的爺爺是根。

技術分享圖片如圖所示,此時B為A的左兒子,要將B旋轉到根。可知A,B,1,2,3的大小關系為3<B<2<A<1。所以旋轉後2變成A的左子樹(如圖)。技術分享圖片

同理,B為A的右兒子,則3<A<2<B<1。所以旋轉後2為A的右兒子技術分享圖片技術分享圖片

至於另外一種情況自己推一下就好啦

事實上,這些旋轉都是將一個點旋轉至它的祖先位置,因此統稱為上旋rotate,實現時多為單旋

OIer們耳熟能詳的Splay操作其實是多次rotate,實現時一般多為雙旋

技術分享圖片
 1 void rotate(int x) {                                                                                //上旋(即左旋和右旋) 
 2     int f=tree[x].fa;
 3     int ff=tree[f].fa;
 4     int lor=ison(x);
 5     tree[ff].son[ison(f)]=x;
 6     tree[x].fa=ff;
 7     tree[f].son[lor]=tree[x].son[lor^1];
 8     tree[tree[x].son[lor^1]].fa=f;
 9     tree[x].son[lor^1]=f;
10     tree[f].fa=x;
11     tree[f].siz=tree[f].num+tree[tree[f].son[0]].siz+tree[tree[f].son[1]].siz;
12     tree[x].siz=tree[x].num+tree[tree[x].son[0]].siz+tree[tree[x].son[1]].siz;
13 }
14 void splay(int x,int goal) {                                                                        //Splay操作,將節點xSplay至節點goal的兒子(若goal為0則Splay至根) 
15     while(tree[x].fa!=goal) {
16         int f=tree[x].fa,ff=tree[f].fa;
17         if(!f||!ff)break;
18         if(ff!=goal) ison(f)^ison(x)?rotate(x):rotate(f);
19         rotate(x);
20     }
21     if(!goal&&tree[x].fa&&!tree[tree[x].fa].fa)rotate(x);
22     if(goal==0)root=x;
23 }
rotate和Splay

插入

和其他平衡樹一樣插入就行了,就是最後的時候將其Splay到根

技術分享圖片
 1 void insert(int x) {                                                                                //插入一個節點並Splay到根 
 2     int u=root,f=0;
 3     for(; u&&tree[u].val!=x; tree[u].siz++,f=u,u=tree[u].son[x>tree[u].val]);
 4     if(u) tree[u].num++,tree[u].siz++;
 5     else {
 6         if(inm)u=mem[inm--];
 7         else u=++ins;
 8         if(f)tree[f].son[x>tree[f].val]=u;
 9         tree[u].fa=f;
10         tree[u].val=x;
11         tree[u].num=tree[u].siz=1;
12     }
13     splay(u,0);
14 }
insert

查找一個數的排名

和其他平衡樹一樣查找,並將其Splay至根。輸出時輸出它的左子樹的大小+1

技術分享圖片
1 void find(int x) {                                                                                    //查詢一個數並Splay到根 
2     int u=root;
3     if(!u)return;
4     for(; tree[u].son[x>tree[u].val]&&x!=tree[u].val; u=tree[u].son[x>tree[u].val]);
5     splay(u,0);
6 }
find

查找排名為x的數

和其他平衡樹一樣查找,並將其Splay至根

技術分享圖片
void finran(int x) {                                                                                //查詢排名為x的數並Splay到根 
    int u=root;
    if(!u)return;
    for(char t; tree[u].son[tree[tree[u].son[0]].siz+tree[u].num<x?1:0]&&(tree[tree[u].son[0]].siz>=x||tree[tree[u].son[0]].siz+tree[u].num<x);
            t=tree[tree[u].son[0]].siz+tree[u].num<x,x-=t*(tree[tree[u].son[0]].siz+tree[u].num),u=tree[u].son[t]);
    splay(u,0);
}
findran

查找一個數的前驅(後繼)

這個簡單,先找到這個數並將其Splay至根,然後沿它的左兒子(前驅)/右兒子(後繼)不斷找右兒子(前驅)/左兒子(後繼)直至沒有兒子

技術分享圖片
1 int nex(int x,int op) {                                                                                //查詢一個數的前驅(0)或後繼(1) 
2     find(x);
3     int u=root;
4     if(tree[u].val>x&&op||tree[u].val<x&&!op||!tree[u].son[op])return u;
5     u=tree[u].son[op];
6     while(tree[u].son[op^1])u=tree[u].son[op^1];
7     return u;
8 }
nex

刪除一個數x

首先找到x的前驅和後繼,並將前驅Splay至根,後繼Splay至前驅的右兒子。由於x的前驅<x<x的後繼,易證此時x為後繼的左兒子且以它為根的子樹大小就是x的個數,直接將其數量減一(x的數量不是1)或將其重置放入內存回收池並將後繼的左兒子設為0(x只有1個)。

技術分享圖片

技術分享圖片
 1 inline void delet(int x) {                                                                            //刪除一個節點 
 2     int la=nex(x,0);
 3     int ne=nex(x,1);
 4     splay(la,0);
 5     if(tree[la].val==x) {
 6         if(tree[la].num>1) {
 7             --tree[la].num;
 8             --tree[la].siz;
 9             return;
10         }
11         root=tree[la].son[1];
12         tree[root].fa=0;
13         mem[++inm]=la;
14         tree[la].res();
15         return;
16     }
17     if(tree[ne].val==x) {
18         splay(ne,0);
19         if(tree[ne].num>1) {
20             --tree[ne].num;
21             --tree[ne].siz;
22             return;
23         }
24         root=tree[ne].son[0];
25         tree[root].fa=0;
26         mem[++inm]=ne;
27         tree[ne].res();
28         return;
29     }
30     splay(ne,la);
31     --tree[la].siz;
32     --tree[ne].siz;
33     int del=tree[ne].son[0];
34     if(tree[del].num>1) {
35         tree[del].num--;
36         --tree[del].siz;
37         splay(del,0);
38     } else {
39         mem[++inm]=tree[ne].son[0];
40         tree[del].res();
41         tree[ne].son[0]=0;
42     }
43 }
delet

例題

洛谷P3369【模板】普通平衡樹

技術分享圖片
 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define INF 0x7fffffff
 4 #define ME 0x7f
 5 #define FO(s) freopen(s".in","r",stdin);freopen(s".out","w",stdout)
 6 #define fui(i,a,b,c) for(int i=(a);i<=(b);i+=(c))
 7 #define fdi(i,a,b,c) for(int i=(a);i>=(b);i-=(c))
 8 #define fel(i,a) for(register int i=h[a];i;i=ne[i])
 9 #define ll long long
10 #define MEM(a,b) memset(a,b,sizeof(a))
11 const int N=100010;
12 int n;struct Node{int fa,son[2],val,num,siz;void res(){fa=son[0]=son[1]=val=num=siz=0;}}tree[N];int ins;int root;int mem[N],inm;
13 template<class T>
14 inline T read(T &n){
15     n=0;int t=1;double x=10;char ch;
16     for(ch=getchar();!isdigit(ch)&&ch!=-;ch=getchar());(ch==-)?t=-1:n=ch-0;
17     for(ch=getchar();isdigit(ch);ch=getchar()) n=n*10+ch-0;
18     if(ch==.) for(ch=getchar();isdigit(ch);ch=getchar()) n+=(ch-0)/x,x*=10;
19     return (n*=t);
20 }char ison(int x){return x==tree[tree[x].fa].son[1];}
21 void rotate(int x){
22     int f=tree[x].fa;int ff=tree[f].fa;int lor=ison(x);tree[ff].son[ison(f)]=x;tree[x].fa=ff;tree[f].son[lor]=tree[x].son[lor^1];tree[tree[x].son[lor^1]].fa=f;tree[x].son[lor^1]=f;
23     tree[f].fa=x;tree[f].siz=tree[f].num+tree[tree[f].son[0]].siz+tree[tree[f].son[1]].siz;tree[x].siz=tree[x].num+tree[tree[x].son[0]].siz+tree[tree[x].son[1]].siz;
24 }void splay(int x,int goal){while(tree[x].fa!=goal){int f=tree[x].fa,ff=tree[f].fa;if(!f||!ff)break;if(ff!=goal) ison(f)^ison(x)?rotate(x):rotate(f);rotate(x);}
25     if(!goal&&tree[x].fa&&!tree[tree[x].fa].fa)rotate(x);if(goal==0)root=x;}
26 void find(int x){int u=root;if(!u)return;for(;tree[u].son[x>tree[u].val]&&x!=tree[u].val;u=tree[u].son[x>tree[u].val]);splay(u,0);}
27 void finran(int x){int u=root;if(!u)return;for(char t;tree[u].son[tree[tree[u].son[0]].siz+tree[u].num<x?1:0]&&(tree[tree[u].son[0]].siz>=x||tree[tree[u].son[0]].siz+tree[u].num<x);
28     t=tree[tree[u].son[0]].siz+tree[u].num<x,x-=t*(tree[tree[u].son[0]].siz+tree[u].num),u=tree[u].son[t]);splay(u,0);
29 }void insert(int x){
30     int u=root,f=0;for(;u&&tree[u].val!=x;tree[u].siz++,f=u,u=tree[u].son[x>tree[u].val]);if(u) tree[u].num++,tree[u].siz++;
31     else{if(inm)u=mem[inm--];else u=++ins;if(f)tree[f].son[x>tree[f].val]=u;tree[u].fa=f;tree[u].val=x;tree[u].num=tree[u].siz=1;}splay(u,0);
32 }int nex(int x,int op){find(x);int u=root;if(tree[u].val>x&&op||tree[u].val<x&&!op||!tree[u].son[op])return u;u=tree[u].son[op];while(tree[u].son[op^1])u=tree[u].son[op^1];return u;}
33 inline void delet(int x){
34     int la=nex(x,0);int ne=nex(x,1);splay(la,0);if(tree[la].val==x){if(tree[la].num>1){--tree[la].num;--tree[la].siz;return;}root=tree[la].son[1];tree[root].fa=0;mem[++inm]=la;tree[la].res();
35     return;}if(tree[ne].val==x){splay(ne,0);if(tree[ne].num>1){--tree[ne].num;--tree[ne].siz;return;}root=tree[ne].son[0];tree[root].fa=0;mem[++inm]=ne;tree[ne].res();return;}splay(ne,la);
36     --tree[la].siz;--tree[ne].siz;int del=tree[ne].son[0];if(tree[del].num>1){tree[del].num--;--tree[del].siz;splay(del,0);}else{mem[++inm]=tree[ne].son[0];tree[del].res();tree[ne].son[0]=0;}
37 }
38 int main(){
39     read(n);
40     fui(i,1,n,1){
41         int opt,x;read(opt);read(x);
42         switch(opt){
43             case 1:{insert(x);break;}case 2:{delet(x);break;}case 3:{find(x);cout<<tree[tree[root].son[0]].siz+1<<endl;break;}
44             case 4:{finran(x);cout<<tree[root].val<<endl;break;}case 5:{cout<<tree[nex(x,0)].val<<endl;break;}case 6:{cout<<tree[nex(x,1)].val<<endl;}
45         }
46     }return 0;
47 }
AC代碼

大概就是這些了

Splay算法摘要