zoukankan      html  css  js  c++  java
  • 2018年8月9号(treap)

      已经两个月没有写博客了,今天突然想写了(其实是在补前的)我一直没有看懂treap;现在有点感觉了,就准备写一篇论文,可能有点low;

      首先,要知道二叉查找树是啥,这就要请度娘:二叉查找树  百度百科

      明白二叉查找树就行;

    接着开始骚操作:  平衡树有7大骚操作: 1.treap旋转(zig/zag)操作    2.treap插入操作    3.treap删除操作    4.求一个元素在treap的前驱    5.求一个元素在treap的后继

    6.在treap中查找排名第K的元素    7.在treap中求元素的排名 

    就这样就从第一个开始讲起:

    首先我要先然大家看懂我的结构体:

    1 struct node{
    2     int l;           //左子节点
    3     int r;          //右子节点
    4     int pri;       //每个节点的权值
    5     int key;     //优先级
    6     int sze;     //以x为根的左子节点的个数
    7     int same; //记录重复节点的个数
    8 }e[N*2];

    1.treap(zig/zag)

      首先一棵treap树要保存一个最小堆的性质而这个最小堆性质就有随机生成的key来维护:

    图中的红色部分就是随机生成的key他要符合最小堆的性质,但是插入一个数他可能会不符合最小堆的性质时就要用到左转右转来维护这个最小堆;

    如:左图他就破坏了堆的性质就要把它左右旋,图中将12进行右旋得到一个符合条件的树;

      zig:

     1 void zig(int &k)                    //这里必须要加上一个&符号处理父子关系
     2 {
     3     int y=e[k].l;                //选取当前结点的左子节点
     4     e[k].l=e[y].r;
     5     e[y].r=k;                    //交换y,k的父子位置,维护BST性质
     6     e[y].sze=e[k].sze;      //维护子树的size
     7     upt(k);                      //更新节点k的size
     8     k=y;
     9 }
    10 //BST就是二叉搜索树

      zag:

    和上面一样操作

     1 void zag(int &k)
     2 {
     3     int y=e[k].r;
     4     e[k].r=e[y].l;
     5     e[y].l=k;
     6     e[y].sze=e[k].sze;
     7     upt(k);
     8     k=y;
     9 }
    10 和上方的zig的操作一样,就不多说了(就是左改成了右)

    2.treap插入操作

       

       给节点随机分配一个优先级,先和二叉排序树的插入一样,先把要插入的点插入到一个叶子上,然后跟维护堆一样,如果当前节点的优先级比根大就旋转,如果当前节点是跟的左儿子就右旋如果当前节点是跟个右儿子就左旋。

      代码实现:

     1 void Insert(int &k,const int &pre)
     2 {
     3     if(!k)
     4     {
     5         k=++pool;e[k].pri=pre;e[k].key=rand();    //rand()是随机函数就是讲树节点的key随机生成
     6         e[k].same=e[k].sze=1;e[k].l=e[k].r=0;    //新建节点
     7         return ;
     8     }
     9     else    ++e[k].sze;
    10     if(e[k].pri==pre)    ++e[k].same;                //重复节点的处理
    11     else if(pre<e[k].pri)
    12     {
    13         Insert(e[k].l,pre);
    14         if(e[e[k].l].key<e[k].key)                      //维护堆的性质
    15         zig(k);
    16     }
    17     else
    18     {
    19         Insert(e[k].r,pre);
    20         if(e[e[k].r].key<e[k].key)                     //同上
    21         zag(k);
    22     }
    23     return ;
    24 }

    3.treap删除操作

      有了旋转的操作之后,Treap的删除比二叉排序树还要简单。因为Treap满足堆性质,所以我们只需要把要删除的节点旋转到叶节点上,然后直接删除就可以了。具体的方法就是每次找到优先级最大的儿子,向与其相反的方向旋转,直到那个节点被旋转到叶节点,然后直接删除。

    代码:

     1 void Delete(int &k,const int &pre)
     2 {
     3     if(e[k].pri==pre)
     4     {
     5         if(e[k].same>1)
     6         {
     7             --e[k].same;
     8             --e[k].sze;
     9         }
    10         else if(!e[k].l||!e[k].r)
    11         k=e[k].l+e[k].r;
    12         else if(e[e[k].l].key<e[e[k].r].key)
    13         {
    14             zig(k);
    15             Delete(k,pre);
    16         }
    17         else
    18         {
    19             zag(k);
    20             Delete(k,pre);
    21         }
    22         return ;
    23     }
    24     --e[k].sze;
    25     if(pre<e[k].pri)
    26     Delete(e[k].l,pre);
    27     else
    28     Delete(e[k].r,pre);
    29     return ;
    30 }

    4.求一个元素在treap的前驱

    1.从根节点开始访问,初始化最优节点为空节点;

    2.如果当前节点的值不大于要求前驱的元素的值,更新最优节点为当前节点,访问当前节点的右子节点;

    3.如果当前节点的值大于要求前驱的元素的值,访问当前节点的左子节点;

    4.如果当前节点是空节点,查找结束,最优节点就是要求的前驱。

    代码:

     1 int qianqu(const int &key)
     2 {
     3     int x=rt,res=-INF;
     4     while(x)
     5     {
     6         if(e[x].pri<key)
     7         {
     8             res=e[x].pri;
     9             x=e[x].r;//要求的前驱为节点x或在节点x的右节点x的右子树内
    10         }
    11         else
    12         x=e[x].l;
    13     }
    14     return res;
    15 }

     5.求一个元素在treap的后继

      和前驱的基本一样:

     1 int houji(const int &key)
     2 {
     3     int x=rt,res=INF;
     4     while(x)
     5     {
     6         if(e[x].pri>key)
     7         {
     8             res=e[x].pri;
     9             x=e[x].l;
    10         }
    11         else
    12         x=e[x].r;
    13     }
    14     return res;
    15 }

    6.在treap中查找排名第K的元素

    如果我们想查找第k小的元素或者询问某个元素在Treap中从小到大的排名时,我们就必须知道每个子树中节点的个数。我们称以一个子树的所有节点的权值之和,为子树的大小。由于插入、删除、旋转等操作,会使每个子树的大小改变,所以我们必须对子树的大小进行动态的维护。

    对于旋转,我们要在旋转后对子节点和根节点分别重新计算其子树的大小。

    对于插入,新建立的节点的子树大小为1。在寻找插入的位置时,每经过一个节点,都要先使以它为根的子树的大小增加1,再递归进入子树查找。

    对于删除,在寻找待删除节点,递归返回时要把所有的经过的节点的子树的大小减少1。要注意的是,删除之前一定要保证待删除节点存在于Treap中。

     1 int di(int &k)
     2 {
     3     int x=rt;
     4     while(x)
     5     {
     6         if(e[e[x].l].sze<k&&e[e[x].l].sze+e[x].same>=k)
     7         return e[x].pri;
     8         if(e[e[x].l].sze>=k)    x=e[x].l;
     9         else
    10         {
    11             k-=e[e[x].l].sze+e[x].same;
    12             x=e[x].r;
    13         }
    14     }
    15     return 0;
    16 }

    7.在treap中求元素的排名 

      

     1 int piming(const int &key)
     2 {
     3     int x=rt,res=0;
     4     while(x)
     5     {
     6         if(key==e[x].pri)
     7         return res+e[e[x].l].sze+1;
     8         if(key<e[x].pri)    x=e[x].l;
     9         else
    10         {
    11             res+=e[e[x].l].sze+e[x].same;
    12             x=e[x].r;
    13         }
    14     }
    15     return res;
    16 }

    这个应该是容易操作的;

    最后贴一整个代码:

      1 #include<bits/stdc++.h>
      2 #define INF 0x7fffff
      3 #define N 100010
      4 using namespace std;
      5 struct node{
      6     int l;
      7     int r;
      8     int pri;
      9     int key;
     10     int sze;
     11     int same;
     12 }e[N*2];
     13 int rt,pool,n;
     14 void upt(const int &k)
     15 {
     16     e[k].sze=e[e[k].l].sze+e[e[k].r].sze+e[k].same;
     17 }
     18 void zig(int &k)
     19 {
     20     int y=e[k].l;
     21     e[k].l=e[y].r;
     22     e[y].r=k;
     23     e[y].sze=e[k].sze;
     24     upt(k);
     25     k=y;
     26 }
     27 void zag(int &k)
     28 {
     29     int y=e[k].r;
     30     e[k].r=e[y].l;
     31     e[y].l=k;
     32     e[y].sze=e[k].sze;
     33     upt(k);
     34     k=y;
     35 }
     36 void Insert(int &k,const int &pre)
     37 {
     38     if(!k)
     39     {
     40         k=++pool;e[k].pri=pre;e[k].key=rand();
     41         e[k].same=e[k].sze=1;e[k].l=e[k].r=0;
     42         return ;
     43     }
     44     else    ++e[k].sze;
     45     if(e[k].pri==pre)    ++e[k].same;
     46     else if(pre<e[k].pri)
     47     {
     48         Insert(e[k].l,pre);
     49         if(e[e[k].l].key<e[k].key)
     50         zig(k);
     51     }
     52     else
     53     {
     54         Insert(e[k].r,pre);
     55         if(e[e[k].r].key<e[k].key)
     56         zag(k);
     57     }
     58     return ;
     59 }
     60 void Delete(int &k,const int &pre)
     61 {
     62     if(e[k].pri==pre)
     63     {
     64         if(e[k].same>1)
     65         {
     66             --e[k].same;
     67             --e[k].sze;
     68         }
     69         else if(!e[k].l||!e[k].r)
     70         k=e[k].l+e[k].r;
     71         else if(e[e[k].l].key<e[e[k].r].key)
     72         {
     73             zig(k);
     74             Delete(k,pre);
     75         }
     76         else
     77         {
     78             zag(k);
     79             Delete(k,pre);
     80         }
     81         return ;
     82     }
     83     --e[k].sze;
     84     if(pre<e[k].pri)
     85     Delete(e[k].l,pre);
     86     else
     87     Delete(e[k].r,pre);
     88     return ;
     89 }
     90 int qianqu(const int &key)
     91 {
     92     int x=rt,res=-INF;
     93     while(x)
     94     {
     95         if(e[x].pri<key)
     96         {
     97             res=e[x].pri;
     98             x=e[x].r;
     99         }
    100         else
    101         x=e[x].l;
    102     }
    103     return res;
    104 }
    105 int houji(const int &key)
    106 {
    107     int x=rt,res=INF;
    108     while(x)
    109     {
    110         if(e[x].pri>key)
    111         {
    112             res=e[x].pri;
    113             x=e[x].l;
    114         }
    115         else
    116         x=e[x].r;
    117     }
    118     return res;
    119 }
    120 int di(int &k)
    121 {
    122     int x=rt;
    123     while(x)
    124     {
    125         if(e[e[x].l].sze<k&&e[e[x].l].sze+e[x].same>=k)
    126         return e[x].pri;
    127         if(e[e[x].l].sze>=k)    x=e[x].l;
    128         else
    129         {
    130             k-=e[e[x].l].sze+e[x].same;
    131             x=e[x].r;
    132         }
    133     }
    134     return 0;
    135 }
    136 int piming(const int &key)
    137 {
    138     int x=rt,res=0;
    139     while(x)
    140     {
    141         if(key==e[x].pri)
    142         return res+e[e[x].l].sze+1;
    143         if(key<e[x].pri)    x=e[x].l;
    144         else
    145         {
    146             res+=e[e[x].l].sze+e[x].same;
    147             x=e[x].r;
    148         }
    149     }
    150     return res;
    151 }
    152 int main()
    153 {
    154     scanf("%d",&n);
    155     for(int i=1;i<=n;i++)
    156     {
    157         int opt,x;
    158         scanf("%d%d",&opt,&x);
    159         if(opt==1)
    160         Insert(rt,x);
    161         if(opt==2)
    162         Delete(rt,x);
    163         if(opt==3)
    164         printf("%d
    ",piming(x));
    165         if(opt==4)
    166         printf("%d
    ",di(x));
    167         if(opt==5)
    168         printf("%d
    ",qianqu(x));
    169         if(opt==6)
    170         printf("%d
    ",houji(x));
    171     }
    172     return 0;
    173 }
  • 相关阅读:
    CMake 从文件路径中提取文件名
    std::multimap 按照key遍历---
    Windows / Linux 一件编译zlib库
    C++ 11 可变模板参数的两种展开方式
    cmake 生成VS项目文件夹
    C++ 利用文件流复制文件
    利用 getsockname 和 getpeername 来获取某一个链接的本地地址和远端地址
    Windows 用VS编译libevent源码
    揭示同步块索引(上):从lock开始
    C手写一个多线程,供java调用
  • 原文地址:https://www.cnblogs.com/zssmg/p/9471092.html
Copyright © 2011-2022 走看看