Splay 學習

  splay:伸展樹

  特點:滿足  左子樹所有值<父親<右子樹所有值

  功能:自平衡樹,可插入,刪除,查詢x的排名,查詢排名為k的值,查詢x的前驅、後繼

  複雜度:log(n)  

  旋轉操作:讓兒子與父親位置互換,但平衡樹的特性不改變,splay可以進行雙旋優化

  以下代碼,查詢前驅和後繼的方法,先用插入一個x,rnk()函數把x旋轉到根節點,再用pre()或next()函數來獲取前驅或後繼。

 

  1 #include<bits/stdc++.h>
  2 using namespace std;
  3 const int N=3e5+7;
  4 
  5 int f[N],cnt[N],ch[N][2],key[N],size[N];
  6 //  父親 個數   孩子     節點值 子樹節點數量
  7 int sz,rt;
  8 
  9 void clear(int x)//清楚一個子樹
 10 {
 11     f[x]=cnt[x]=ch[x][0]=ch[x][1]=size[x]=key[x]=0;
 12 }
 13 
 14 bool get(int x)//確定本節點是父親的左孩子或右孩子
 15 {
 16     return ch[f[x]][1]==x;
 17 }
 18 
 19 void pushup(int x)//更新節點信息
 20 {
 21     if(x)
 22     {
 23         size[x]=cnt[x];//本節點的數量
 24         if(ch[x][0])
 25             size[x]+=size[ch[x][0]];//左子樹的數量
 26         if(ch[x][1])
 27             size[x]+=size[ch[x][1]];//右子樹的數量
 28     }
 29 }
 30 
 31 void rotate(int x)
 32 {
 33     int old=f[x],oldf=f[old],which=get(x);//which為本節點與父節點的關係
 34 
 35     ch[old][which]=ch[x][which^1];//本節點的“反which“儿子 成為 父親的”which“儿子
 36     f[ch[old][which]]=old;
 37 
 38     ch[x][which^1]=old;//父親成為x的“反which”儿子
 39     f[old]=x;
 40 
 41     f[x]=oldf;//x的父親是祖父
 42     if(oldf)
 43         ch[oldf][ch[oldf][1]==old]=x;//祖父的儿子是x
 44 
 45     pushup(old);//更新
 46     pushup(x);
 47 
 48 }
 49 
 50 void splay(int x)
 51 {
 52     for(int fa;fa=f[x];rotate(x))
 53         if(f[fa])
 54             rotate(get(x)==get(fa)?fa:x);//雙旋優化,三點一線,先旋父親
 55     rt=x;//更新为根节点
 56 }
 57 
 58 void insert(int x)
 59 {
 60     if(rt==0)//如果空樹
 61     {
 62         sz++;
 63         key[sz]=x;
 64         rt=sz;
 65         cnt[sz]=size[sz]=1;
 66         f[sz]=ch[sz][0]=ch[sz][1]=0;
 67         return;
 68     }
 69     int now=rt,fa=0;
 70     while(1)
 71     {
 72         if(x==key[now])//如果x已存在,節點上的個數++
 73         {
 74             cnt[now]++;
 75             pushup(now);
 76             pushup(fa);
 77             splay(now);
 78             return;
 79         }
 80         fa=now;
 81         now=ch[now][x>key[now]];//根據平衡樹特性往下搜
 82         if(!now)//如果不存在x,創建節點
 83         {
 84             sz++;
 85             size[sz]=cnt[sz]=1;
 86             ch[sz][0]=ch[sz][1]=0;
 87             ch[fa][x>key[fa]]=sz;
 88             f[sz]=fa;
 89             key[sz]=x;
 90             pushup(fa);
 91             splay(sz);
 92             return;
 93         }
 94     }
 95 }
 96 
 97 int rnk(int x)
 98 {
 99     int now=rt,ans=0;
100     while(now)
101     {
102         if(x<key[now])//如果比x的值,搜左子樹
103             now=ch[now][0];
104         else
105         {
106             ans+=size[ch[now][0]];//否則先加上左子樹節點的數量
107             if(x==key[now])//如果x為所查詢節點
108             {
109                 splay(now);
110                 return ans+1;
111             }
112             ans+=cnt[now];//x不是所查詢節點
113             now=ch[now][1];
114         }
115     }
116     printf("Not find\n");//如果不存在該節點
117     return -1;
118 }
119 
120 int kth(int x)
121 {
122     int now=rt;
123     while(now)
124     {
125         if(ch[now][0]&&x<=size[ch[now][0]])//如果左子树左子樹數量>當前排名
126             now=ch[now][0];
127         else
128         {
129             int temp=size[ch[now][0]]+cnt[now];
130             if(x<=temp)//左子樹加上本節點數量大於排名,本節點即所求
131                 return key[now];
132             x-=temp;//往右子樹搜
133             now=ch[now][1];
134         }
135     }
136     printf("Not find\n");
137     return -1;
138 }
139 
140 int pre()//先把所求的節點splay位根節點,再用此函數得到前驅
141 {
142     int now=ch[rt][0];
143     while(ch[now][1])
144         now=ch[now][1];
145     return now;
146 }
147 int next()//先splay,再得到後繼
148 {
149     int now=ch[rt][1];
150     while(ch[now][0])
151         now=ch[now][0];
152     return now;
153 }
154 void del(int x)
155 {
156     int k=rnk(x);//先splay到根
157     if(k==-1)//如果不存在x
158         return ;
159     if(cnt[rt]>1)//若x的數量>1。只需要數量--
160     {
161         cnt[rt]--;
162         pushup(rt);
163         return;
164     }
165     if(!ch[rt][0]&&!ch[rt][1])//如果沒有孩子,直接清除
166     {
167         clear(rt);
168         rt=0;
169         return;
170     }
171     if(!ch[rt][0])//如果只有一邊孩子,直接讓孩子頂替根節點
172     {
173         int oldrt=rt;
174         rt=ch[rt][1];
175         f[rt]=0;
176         clear(oldrt);
177         return;
178     }
179     else if(!ch[rt][1])
180     {
181         int oldrt=rt;
182         rt=ch[rt][0];
183         f[rt]=0;
184         clear(oldrt);
185         return;
186     }
187     int oldrt=rt,leftbig=pre();//兩邊都有,則讓前驅作為根節點
188     splay(leftbig);
189     ch[rt][1]=ch[oldrt][1];//做相應的連接
190     f[ch[oldrt][1]]=rt;
191     clear(oldrt);
192     pushup(rt);
193 }
194 
195 void show(int x)//升序輸出
196 {
197     if(ch[x][0])
198         show(ch[x][0]);
199     for(int i=0;i<cnt[x];++i)
200         printf("%d ",key[x]);
201     if(ch[x][1])
202         show(ch[x][1]);
203 }
204 int main()
205 {
206     int x;
207     char f[5];
208     while(1)
209     {
210         scanf("%s",f);
211         if(f[0]=='q')
212             break;
213         else if(f[0]=='a')//插入
214         {
215             scanf("%d",&x);
216             insert(x);
217         }
218         else if(f[0]=='d')//刪除
219         {
220             scanf("%d",&x);
221             del(x);
222         }
223         else if(f[0]=='r')//查詢x的排名
224         {
225             scanf("%d",&x);
226             printf("rank : %d\n",rnk(x));
227         }
228         else if(f[0]=='k')//查詢排名為k的值
229         {
230             scanf("%d",&x);
231             printf("-> %d\n",kth(x));
232         }
233         puts("");
234         printf("Now : ");
235         show(rt);
236         puts("");
237         puts("");
238     }
239 }
基本Splay

 

posted @ 2018-11-20 17:05  Lin88  阅读(113)  评论(0编辑  收藏  举报