• 旋转/非旋转treap的简单操作


    treap(树堆)

    是在二叉搜索树的基础上,通过维护随机附加域,使其满足堆性质,从而使树相对平衡的二叉树;

    为什么可以这样呢?

    因为在维护堆的时候可以同时保证搜索树的性质;

    (比如当一棵树的一个域满足堆的性质时,只要不断的互换左右,她的另一个域总会满足搜索树的性质)

    (当一棵树的一个域满足搜索树的性质时,只要不断的上下旋转,她的另一个域总会满足二叉堆的性质)

    于是树堆有两种实现:

    旋转treap:

    她的操作基于旋转;

    注意:当存在相同val时,对于旋转treap,是存在一个节点中的;

    roll(旋转操作)

    交换点x与她的x.ch(i):

    fa(x).ch(i)=x.ch(i);

    x.ch(i)=x.ch(i).ch(i^1);

    x.ch(i).ch(i^1)=x;

    以上操作是一个改变上下位置却不影响搜索树性质的方法,与splay类似;

    1 void roll(int &now){
    2     int wh=data[data[now].ch[0]].key<data[data[now].ch[1]].key?0:1;
    3     int son=data[now].ch[wh];
    4     data[now].ch[wh]=data[son].ch[wh^1];
    5     data[son].ch[wh^1]=now;
    6     up(now);
    7     now=son;
    8 }
    View Code

    insert(插入操作)

    普通的搜索树的插入操作,是插入一个叶子;

    普通的堆也是插入一个叶子,然后再把她旋转到相应的位置上;

    这样的话,只要按搜索树的法则插入一个节点,然后再以不影响搜索树性质的方式把她旋转到符合堆性质的位置即可;

     1 void insert(int &now){
     2     if(now==0){
     3         now=make_data(x);
     4         return;
     5     }
     6     if(data[now].value==x){
     7         data[now].cnt++;
     8         data[now].size++;
     9     }
    10     else{
    11         int wh=x < data[now].value ? 0 : 1;
    12         insert(data[now].ch[wh]);
    13         if(data[now].key>=data[data[now].ch[wh]].key)
    14             roll(now);
    15     }
    16     up(now);
    17 }
    View Code

    del(删除操作)

    与堆类似的;

    把她通过旋转操作下沉到叶子节点,然后再断开即可;

     1 void del(int &now){
     2     if(data[now].value==x){
     3         if(data[now].cnt==1){
     4             if(data[now].ch[0]*data[now].ch[1]==0){
     5                 now=data[now].ch[1]+data[now].ch[0];
     6                 return ;
     7             }
     8             roll(now);
     9             int wh=data[data[now].ch[0]].value==x?0:1;
    10             del(data[now].ch[wh]);
    11         }
    12         else{
    13             data[now].size--;    data[now].cnt--;
    14         }
    15     }
    16     else{
    17         int wh=data[now].value>x?0:1;
    18         del(data[now].ch[wh]);
    19     }
    20     up(now);
    21 }
    View Code

    这两个操作是与堆流程类似的操作(虽然作为c++党我不写堆);

    还有寻找Kth number,寻找rank,寻找last、next等等与平衡树相关的操作,不在此处赘述;

    局限性:不能完成区间操作;

    有关代码:

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

      1 #include<cstdio>
      2 #include<cstdlib>
      3 using namespace std;
      4 #define INF 2147483647
      5 int n;
      6 struct poo
      7 {
      8     int size,value,key,cnt;
      9     int ch[2];
     10 }data[100001];
     11 int tot,root,x;
     12 int  make_data(int );
     13 void insert(int&);
     14 void roll(int&);
     15 int  find(    );
     16 int  rank(    );
     17 void del(int&);
     18 int  las(int );
     19 int  nex(int );
     20 void up(int );
     21 int main()
     22 {
     23     int i,j;
     24     data[0].value=INF;data[0].key=INF;
     25     scanf("%d",&n);
     26     for(i=1;i<=n;i++){
     27         scanf("%d%d",&j,&x);
     28         switch(j){
     29             case 1:insert(root);break;
     30             case 2:   del(root);break;
     31             case 3:  printf("%d
    ",rank(    ));break;
     32             case 4:  printf("%d
    ",find(    ));break;
     33             case 5:  printf("%d
    ", las(root));break;
     34             case 6:  printf("%d
    ", nex(root));break;
     35         }
     36     }
     37     return 0;
     38 }
     39 int make_data(int value){
     40     tot++;
     41     data[tot].cnt++;
     42     data[tot].key=(rand()/2+rand()/2);
     43     data[tot].size=1;
     44     data[tot].value=value;
     45     return tot;
     46 }
     47 void insert(int &now){
     48     if(now==0){
     49         now=make_data(x);
     50         return;
     51     }
     52     if(data[now].value==x){
     53         data[now].cnt++;
     54         data[now].size++;
     55     }
     56     else{
     57         int wh=x < data[now].value ? 0 : 1;
     58         insert(data[now].ch[wh]);
     59         if(data[now].key>=data[data[now].ch[wh]].key)
     60             roll(now);
     61     }
     62     up(now);
     63 }
     64 void roll(int &now){
     65     int wh=data[data[now].ch[0]].key<data[data[now].ch[1]].key?0:1;
     66     int son=data[now].ch[wh];
     67     data[now].ch[wh]=data[son].ch[wh^1];
     68     data[son].ch[wh^1]=now;
     69     up(now);
     70     now=son;
     71 }
     72 int find(){
     73     int now=root;
     74     int ls,rs;
     75     ls=data[now].ch[0];rs=data[now].ch[1];
     76     while(x<=data[ls].size||x>data[now].size-data[rs].size){
     77         if(data[ls].size>=x)
     78             now=ls;
     79         else{
     80             x=x+data[rs].size-data[now].size;
     81             now=rs;
     82         }
     83         ls=data[now].ch[0];rs=data[now].ch[1];
     84     }
     85     return data[now].value;
     86 }
     87 int rank(){
     88     int now=root,ans=0;
     89     int ls=data[now].ch[0],rs=data[now].ch[1];
     90     while(x!=data[now].value&&x!=0)
     91     {
     92         if(x<data[now].value)
     93             now=ls;
     94         else{
     95             ans+=data[now].size-data[rs].size;
     96             now=rs;
     97         }
     98         ls=data[now].ch[0];rs=data[now].ch[1];
     99     }
    100     return ans+data[ls].size+1;
    101 }
    102 void del(int &now){
    103     if(data[now].value==x){
    104         if(data[now].cnt==1){
    105             if(data[now].ch[0]*data[now].ch[1]==0){
    106                 now=data[now].ch[1]+data[now].ch[0];
    107                 return ;
    108             }
    109             roll(now);
    110             int wh=data[data[now].ch[0]].value==x?0:1;
    111             del(data[now].ch[wh]);
    112         }
    113         else{
    114             data[now].size--;    data[now].cnt--;
    115         }
    116     }
    117     else{
    118         int wh=data[now].value>x?0:1;
    119         del(data[now].ch[wh]);
    120     }
    121     up(now);
    122 }
    123 int las(int now){
    124     int ans=0,an=0;
    125     if(!now)return 0;
    126     if(data[now].value<x){
    127         ans=data[now].value;
    128         an=las(data[now].ch[1]);
    129         ans=an!=0?an:ans;
    130     }
    131     else{
    132         ans=las(data[now].ch[0]);
    133     }
    134     return ans;
    135 }
    136 int nex(int now){
    137     int ans=0,an=0;
    138     if(!now)return 0;
    139     if(data[now].value>x){
    140         ans=data[now].value;
    141         an=nex(data[now].ch[0]);
    142         ans=an!=0?an:ans;
    143     }
    144     else{
    145         ans=nex(data[now].ch[1]);
    146     }
    147     return ans;
    148 }
    149 void up(int now){
    150     data[now].size=data[data[now].ch[0]].size+data[data[now].ch[1]].size+data[now].cnt;
    151 }
    152 //treap on the 2017.1.21
    153 //10
    154 //1 5
    155 //4 1
    156 //1 6
    157 //1 7
    158 //1 10
    159 //1 3
    160 //1 4
    161 //6 2
    162 //1 8
    163 //5 9
    164 //
    165 //14
    166 //1 5
    167 //1 6
    168 //1 7
    169 //1 10
    170 //1 3
    171 //1 4
    172 //1 8
    173 //3 3
    174 //3 4
    175 //3 5
    176 //3 6
    177 //4 5
    178 //4 6
    179 //4 7
    View Code

    非旋转treap

    同样是树堆,旋转treap通过与堆类似的旋转操作维护,但非旋转treap,通过与平衡树类似的拆分|合并操作完成;

    注意:当存在相同val时,对于非旋转treap,是存在不同节点中的;

    split(拆分操作)

    这里介绍按value拆分(还有按排名拆分不讲)

    首先预留两个变量名作为两棵树的树根名;

    然后从原树根开始,按查找value为k的点的方法往下走,对于每一个走到的点,她大于value则属于右树,否则属于左树,

    因为是从上往下走的,所以后进入树的点是先入者的子节点(符合堆性质)

    因为当x.val>value时下一步走x.ls,之后的点全比x小,所以把x接到右树后,下一个接入右树的点——不管是谁——应当变成x的左儿子;

    对x.val≤value,也是相似的;

     1 void split(int now,int&ltr,int&rtr,int value){
     2     if(!now){
     3         ltr=rtr=0;
     4         return;
     5     }
     6     if(data[now].val<=value)
     7         ltr=now,split(data[now].ch[1],data[ltr].ch[1],rtr,value);
     8     else
     9         rtr=now,split(data[now].ch[0],ltr,data[rtr].ch[0],value);
    10     up(now);
    11 }
    View Code

    merge(合并操作)

    这里介绍当treapA的所有节点val小于treapB时的操作(即通常使用的操作)

    可以看出只要把A的右链和B的左链,拆开,按符合堆上下顺序排好,再连上符合搜索树的父子关系即可;

    因为AB分别满足堆性质,所以所谓的“按符合堆上下顺序排好”,只要互相参差插入即可,不改变A,B各自的相对上下顺序;

    连父子关系时,属于A树的点需要连一个rs(因为排在她下方的点都比她大)属于B树的点需要连一个ls(因为排在她下方的点都比她小)

    实现是递归的

    merge( &now , a , b )-->( a.key < b.key ? ( now=a.rs , merge( &now.rs , a.rs , b ) ) : ( now=b.ls , merge( &now.ls , a , b.ls ) ) );

     1 void merge(int&now,int s,int b){
     2     if(!s||!b){
     3         now=s+b;return;
     4     }
     5     if(data[s].key<data[b].key)
     6         now=s,merge(data[now].ch[1],data[s].ch[1],b);
     7     else
     8         now=b,merge(data[now].ch[0],s,data[b].ch[0]);
     9     up(now);
    10 }
    View Code

    insert(value):把树split成A树≤value,B树>value,然后建一个value的点x,然后merge(root,A,x),merge(root,root,B);

    del(value):把树拆成三部分,中间是等于value的点——把这部分的点减少一个,然后再合并树的三部分;

    非旋转treap是一种非常好的平衡树,她有treap的优良性质——常数比Splay小,而且还支持区间操作和可持久化,

    重点是代码短

    唯一的缺点的是不好理解

    有关代码:

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

      1 #include<cstdio>
      2 #include<cstdlib>
      3 using namespace std;
      4 const int INF=2147483600;
      5 struct Treap{
      6     int val,key,size;
      7     int ch[2];
      8 }data[200010];
      9 int root,tot;
     10 void make_data(int&,int );
     11 void up(int );
     12 void merge(int&,int,int);
     13 void split(int ,int&,int&,int );
     14 void insert(int );
     15 void del(int );
     16 void rank(int );
     17 void find(int ,int );
     18 int  las(int );
     19 int  nex(int );
     20 int main()
     21 {
     22     srand(2.17);
     23     int i,j,k,m;
     24     root=0;data[0].key=INF;data[0].val=INF;data[0].size=0;
     25     scanf("%d",&m);
     26     for(i=1;i<=m;i++){
     27         scanf("%d%d",&j,&k);
     28         if(j==1)insert(k);
     29         if(j==2)del(k);
     30         if(j==3)rank(k);
     31         if(j==4)find(root,k);
     32         if(j==5)las(k);
     33         if(j==6)nex(k);
     34     }
     35     return 0;
     36 }
     37 void make_data(int&now,int value){
     38     data[++tot].val=value;data[tot].key=rand();
     39     data[tot].size=1;
     40     data[tot].ch[0]=data[tot].ch[1]=0;
     41     now=tot;
     42 }
     43 void up(int now){
     44     data[now].size=data[data[now].ch[0]].size+data[data[now].ch[1]].size+1;
     45 }
     46 void merge(int&now,int s,int b){
     47     if(!s||!b){
     48         now=s+b;return;
     49     }
     50     if(data[s].key<data[b].key)
     51         now=s,merge(data[now].ch[1],data[s].ch[1],b);
     52     else
     53         now=b,merge(data[now].ch[0],s,data[b].ch[0]);
     54     up(now);
     55 }
     56 void split(int now,int&ltr,int&rtr,int value){
     57     if(!now){
     58         ltr=rtr=0;
     59         return;
     60     }
     61     if(data[now].val<=value)
     62         ltr=now,split(data[now].ch[1],data[ltr].ch[1],rtr,value);
     63     else
     64         rtr=now,split(data[now].ch[0],ltr,data[rtr].ch[0],value);
     65     up(now);
     66 }
     67 void insert(int value){
     68     int x=0,y=0,z=0;
     69     make_data(z,value);
     70     split(root,x,y,value);
     71     merge(x,x,z);
     72     merge(root,x,y);
     73 }
     74 void del(int value){
     75     int x=0,y=0,z=0;
     76     split(root,x,y,value);
     77     split(x,x,z,value-1);
     78     merge(z,data[z].ch[0],data[z].ch[1]);
     79     merge(x,x,z);merge(root,x,y);
     80 }
     81 void rank(int value){
     82     int x=0,y=0;
     83     split(root,x,y,value-1);
     84     printf("%d
    ",data[x].size+1);
     85     merge(root,x,y);
     86 }
     87 void find(int now,int x){
     88     while(data[data[now].ch[0]].size+1!=x){
     89         if(data[data[now].ch[0]].size>=x)
     90             now=data[now].ch[0];
     91         else
     92             x-=(data[data[now].ch[0]].size+1),now=data[now].ch[1];
     93     }
     94     printf("%d
    ",data[now].val);
     95 }
     96 int  las(int value){
     97     int x=0,y=0;
     98     split(root,x,y,value-1);
     99     find(x,data[x].size);
    100     merge(root,x,y);
    101 }
    102 int  nex(int value){
    103     int x=0,y=0;
    104     split(root,x,y,value);
    105     find(y,1);
    106     merge(root,x,y);
    107 }
    View Code
  • 相关阅读:
    开源Jabber(XMPP) IM服务器介绍
    ejabberd、jabber、jabberd、xmpp辨析
    分布式与集群的区别
    浅谈Javascript事件模拟
    理清javascript的相关概念 DOM和BOM
    js基础学习第一天(关于DOM和BOM)一
    处理机调度和死锁
    C++11 之 " = delete "
    小数的二进制表示
    二进制数的插入
  • 原文地址:https://www.cnblogs.com/nietzsche-oier/p/6748292.html
Copyright © 2020-2023  润新知