平衡树——Treap,Splay

二叉查找树(BST)的两条性质:

 

    1.该节点的关键码不小于它的左子树中任意节点的关键码

 

    2.该节点的关键码不大于它的右子树中任意节点的关键码

 

显然,二叉树的中序遍历是关键码单调递增排列的序列。

 

为避免越界,在BST中插入关键码为正无穷和负无穷的结点为初始情况。

 

 

基本操作:

 

1.检索

 

   递归:

   (1)找到结点,返回值

   (2)未找到节点且无路可走(子结点为空),返回

   (3)结点偏大,向左继续递归

   (4)结点偏小,向右继续递归

   ps:也可以用循环写,只是我习惯用深搜而已

 

2.插入

   

   若发现关键值的结点不存在时,新建节点。

 

3.排名(Kth)

 

   根据平衡树的性质,一个结点的排名即左子树大小+1。

 

4.前驱/后继

   首先找到结点,根据平衡树的性质:

    若找其前驱,先向左走一步(比结点小的左子树中找),随

    后不断向右走直到叶节点,即为比结点小的数中最大的一个。

    后既同理。

    

5.删除

    

   首先,检索得到结点。

      若p的子结点个数小于2,则直接删除p,令子结点直接代替其 位置,与p的父节点相连。

      若p既有左子树又有右子树,则求出p的后继结点next。若next 没有左子树,直接删除next,并令next的右子树代替next的位置。 最后, 再让next节点代替p结点, 删除p即可。

 

 

Treap(单旋转)

 

下面是用treap写普通平衡树(BZOJ3224)的代码,代码几乎是李煜东的《算法竞赛进阶指南》上面的样子(最喜欢的一本书)。

 

  1 #include<iostream>
  2 #include<cstdlib>
  3 using namespace std;
  4 const int SIZE = 100005;
  5 struct Treap{
  6     int l, r;
  7     int val, dat;
  8     int size, cnt;
  9 }a[SIZE];
 10 int tot, n, root, INF = 0x7fffffff;
 11 int New(int val){
 12     a[++tot].val = val;
 13     a[tot].size = a[tot].cnt = 1;
 14     a[tot].dat = rand();
 15     return tot;
 16 }
 17 void Update(int p){
 18     a[p].size = a[a[p].l].size + a[a[p].r].size + a[p].cnt;
 19 }
 20 
 21 void Build(){
 22     New(-INF), New(INF);
 23     root = 1, a[1].r = 2;
 24     Update(root);
 25 }
 26 
 27 void zig(int &p){
 28     int q = a[p].l;
 29     a[p].l = a[q].r, a[q].r = p, p = q;
 30     Update(a[p].r), Update(p);
 31 }
 32 void zag(int &p){
 33     int q = a[p].r;
 34     a[p].r = a[q].l, a[q].l = p, p = q;
 35     Update(a[p].l), Update(p);
 36 }
 37 void Insert(int &p, int val){
 38     if(p == 0) {
 39         p = New(val);
 40         return ;
 41     }
 42     if(val == a[p].val){
 43         a[p].cnt++, Update(p);
 44         return ;
 45     }
 46     if(val < a[p].val){
 47         Insert(a[p].l, val);
 48         if(a[p].dat < a[a[p].l].dat) zig(p);
 49     }
 50     else{
 51         Insert(a[p].r, val);
 52         if(a[p].dat < a[a[p].r].dat) zag(p); 
 53     }
 54     Update(p);
 55 }
 56 void Remove(int &p, int val){
 57     if(p == 0) return;
 58     if(val == a[p].val){
 59         if(a[p].cnt > 1){
 60             a[p].cnt--, Update(p);
 61             return;
 62         }
 63         if(a[p].l || a[p].r){
 64             if(a[p].r == 0 || a[a[p].l].dat > a[a[p].r].dat) 
 65                 zig(p), Remove(a[p].l, val);
 66             else 
 67                 zag(p), Remove(a[p].r, val);
 68             Update(p);
 69         }else p = 0;
 70     }
 71     val < a[p].val ? Remove(a[p].l, val) : Remove(a[p].r, val);
 72     Update(p); 
 73 }
 74 int GetRankByVal(int p, int val){
 75     if(p == 0) return 0;
 76     if(a[p].val == val) return a[a[p].l].size;
 77     if(val < a[p].val) return GetRankByVal(a[p].l, val);
 78     return GetRankByVal(a[p].r, val) + a[a[p].l].size + a[p].cnt;
 79 } 
 80 int GetValByRank(int p, int rank){
 81     if(p == 0) return INF;
 82     if(a[a[p].l].size >= rank) return GetValByRank(a[p].l, rank);
 83     if(a[a[p].l].size + a[p].cnt >= rank) return a[p].val;
 84     return GetValByRank(a[p].r, rank - a[a[p].l].size - a[p].cnt);
 85 }
 86 
 87 int GetPrev(int val){
 88     int ans = 1;
 89     int p = root;
 90     while(p){
 91         if(val == a[p].val){
 92             if(a[p].l > 0){
 93                 p = a[p].l;
 94                 while(a[p].r) p = a[p].r;
 95                 ans = p;
 96             }
 97             break;
 98         }
 99         if(a[p].val < val && a[p].val > a[ans].val) ans = p;
100         p = val < a[p].val ? a[p].l : a[p].r; 
101     }
102     return a[ans].val;
103 }
104 int GetNext(int val){
105     int ans = 2;
106     int p = root;
107     while(p){
108         if(val == a[p].val){
109             if(a[p].r > 0){
110                 p = a[p].r;
111                 while(a[p].l) p = a[p].l;
112                 ans = p;
113             }
114             break;
115         }
116         if(a[p].val > val && a[p].val < a[ans].val) ans = p;
117         p = val < a[p].val ? a[p].l : a[p].r;
118     }
119     return a[ans].val;
120 }
121 
122 int main(){
123     Build();
124     int n;
125     cin >> n;
126     while(n--){
127         int f, x;
128         cin >> f >> x;
129         switch(f){
130             case 1:
131                 Insert(root, x);
132                 break;
133             case 2:
134                 Remove(root, x);
135                 break;
136             case 3:
137                 cout << GetRankByVal(root, x) <<"\n";
138                 break;
139             case 4:
140                 cout << GetValByRank(root, x+1) <<"\n";
141                 break;
142             case 5:
143                 cout << GetPrev(x) <<"\n";
144                 break;
145             case 6:
146                 cout << GetNext(x) <<"\n";
147                 break;
148         }
149     }
150     return 0;
151 }

splay(伸展树)

 

   伸展树(Splay Tree),也叫分裂树,是一种二叉排序树,它能在O(log n)内完成插入、查找和删除操作。

   概括的说其操作——把一个结点旋转到根。

   概括的说其转法——如果结点是父结点的左子节点,则进行右旋;如果结点是父节点的右子节点,则进行左旋。

   

   逐渐可以理解转的原理的。

   

  1 #include<bits/stdc++.h>
  2    using namespace std;
  3    
  4    const int _=1e5+20;
  5    
  6    int n,opt,x;
  7    
  8    struct Splay
  9    {
 10        int root,cnt,val[_],ch[_][2],siz[_],sz[_],par[_];
 11    
 12        bool chk(int x)
 13        {
 14            return ch[par[x]][1]==x;
 15        }
 16    
 17        void pushup(int x)
 18        {
 19            if (!x) return;
 20            siz[x]=siz[ch[x][0]]+siz[ch[x][1]]+sz[x];
 21            //cout<<x<<' '<<siz[x]<<"       ";
 22        }
 23    
 24        int newnode(int x)
 25        {
 26            ++cnt;
 27            val[cnt]=x,sz[cnt]=siz[cnt]=1;
 28            ch[cnt][0]=ch[cnt][1]=0;
 29            return cnt;
 30        }
 31    
 32        void rotate(int x)
 33        {
 34            int y=par[x],z=par[y],k=chk(x),w=ch[x][k^1];
 35            ch[y][k]=w,par[w]=y;
 36            ch[z][chk(y)]=x,par[x]=z;
 37            ch[x][k^1]=y,par[y]=x;
 38            pushup(y),pushup(x);
 39        }
 40    
 41        void splay(int x,int goal=0)
 42        {
 43            while (par[x]!=goal)
 44            {
 45                int y=par[x],z=par[y];
 46                if (z!=goal)
 47                {
 48                    if (chk(x)==chk(y)) rotate(y);
 49                    else rotate(x);    
 50                }
 51                rotate(x);
 52            }
 53            if (!goal) root=x;
 54        }
 55    
 56        void insert(int x)
 57        {
 58            int cur=root,p=0;
 59            while (cur && x!=val[cur]) p=cur,cur=ch[cur][x>val[cur]];
 60            if (cur) ++sz[cur],splay(cur);
 61            else cur=ch[p][x>val[p]]=newnode(x),par[cur]=p,splay(cur);
 62        }
 63    
 64        void find(int x)
 65        {
 66            if (!root) return;
 67            int cur=root;
 68            while (ch[cur][x>val[cur]] && x!=val[cur]) cur=ch[cur][x>val[cur]];
 69            splay(cur);
 70        }
 71    
 72        int rank(int x)
 73        {
 74            find(x);
 75            return siz[ch[root][0]]+1;
 76        }
 77    
 78        int kth(int k)
 79        {
 80            int cur=root;
 81            while (true)
 82            {
 83                if (ch[cur][0] && k<=siz[ch[cur][0]]) cur=ch[cur][0];
 84                else if (k>siz[ch[cur][0]]+sz[cur]) k-=siz[ch[cur][0]]+sz[cur],cur=ch[cur][1];
 85                else return cur;
 86            }
 87        }
 88    
 89        int pre(int x)
 90        {
 91            find(x);
 92            if (val[root]<x) return root;
 93            int cur=ch[root][0];
 94            while (ch[cur][1]) cur=ch[cur][1];
 95            return cur;
 96        }
 97    
 98        int suc(int x)
 99        {
100            find(x);
101            if (val[root]>x) return root;
102            int cur=ch[root][1];
103            while (ch[cur][0]) cur=ch[cur][0];
104            return cur;
105        }
106    
107        void remove(int x)
108        {
109            int u=pre(x),v=suc(x);
110            //cout<<u<<' '<<v<<endl;
111            splay(u),splay(v,u);
112            int w=ch[v][0];
113            if (sz[w]>1) --sz[w],splay(w);
114            else siz[0]=sz[0]=ch[v][0]=0;
115            //pushup(v),pushup(u);
116        }
117    
118        void maintain(int x)
119        {
120            if (!ch[x][0] && !ch[x][1]) siz[x]=sz[x];
121            if (ch[x][0]) maintain(ch[x][0]);
122            if (ch[x][1]) maintain(ch[x][1]);
123            pushup(x);
124        }
125    
126        void print(int x)
127        {
128            cout<<x<<' '<<val[x]<<' '<<sz[x]<<' '<<siz[x]<<' '<<ch[x][0]<<' '<<ch[x][1]<<' '<<endl;
129            if (ch[x][0]) print(ch[x][0]);
130            if (ch[x][1]) print(ch[x][1]);
131        }
132    }T;
133    
134    int main()
135    {
136        T.insert(-0x3f3f3f3f);T.insert(0x3f3f3f3f);
137        cin>>n;
138        for (int i=1; i<=n; i++)
139        {
140            cin>>opt>>x;
141            if (opt==1) T.insert(x);
142            else if (opt==2) T.remove(x);
143            else if (opt==3) cout<<T.rank(x)-1<<'\n';
144            else if (opt==4) cout<<T.val[T.kth(x+1)]<<'\n';
145            else if (opt==5) cout<<T.val[T.pre(x)]<<'\n';
146            else if (opt==6) cout<<T.val[T.suc(x)]<<'\n';
147     }
148        return 0;
149    }

 

posted @ 2019-08-15 00:06  hélium  阅读(268)  评论(0编辑  收藏  举报