• Treap 代码实现及其原理


      Treap是一颗拥有键值,优先级两种权值的树.

      对于键值而言,这棵树是排序二叉树(BST Balance Sort Tree);

      对于优先级而言,这棵树是堆,既在这棵树的任意子树中,根节点的优先级是最大的(这个性质称为堆性质)

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<cstdlib>
     4 #include<algorithm>
     5 using namespace std;
     6 struct Node{
     7     Node *ch[2]; //左右子树 
     8     int r;        // 优先级,数值越大,优先级越高
     9     int v;    //
    10     bool operator < (const Node &rhs ) const{
    11         return r < rhs.r;    //根据优先级比较节点    
    12     } 
    13     int cmp(int x) const{
    14         if( x == v ) return -1;
    15         return x < v ? 0 : 1;    
    16     }
    17 };
    18 // 注意代码中的小技巧
    19 // 因为 d = 0 | 1, d^1 = 1-d, 但计算速度更快。
    20 // 另一个关键之处在于o的类型。
    21 // 首先,o是指向一个Treap节点的指针
    22 // 因此为Node *类型,其次,O本身是可以修改的
    23 // 因此c是引用。若非如此,旋转后指针指向将不改变 
    24 void rotate( Node* &o, int d ){
    25     // d = 0: 左旋, d = 1: 右旋 
    26     Node *k = o->ch[d^1];
    27     o->ch[d^1] = k->ch[d];
    28     k->ch[d] = o;
    29     o = k;
    30 }
    31 // 插入节点时,首先随即给新节点一个优先级
    32 // 然后执行普通的插入算法.(根据键值大小判断在哪颗子树上)
    33 // 执行完毕后用左右旋让这个节点往上走,从而维持堆性质.
    34 
    35 // Treap的插入操作分为 BST插入 与 旋转 两个部分.
    36 // 若采用递归实现,则只需在递归插入后判断是否需要旋转.
    37 
    38 // 在以 o 为根的子树中插入键值x,修改o
    39 void insert( Node* &o, int x ){
    40     if( o == NULL ){
    41         o = new Node();
    42         o->ch[0] = o->ch[1] = NULL;
    43         o->v = x; o->r = rand();    
    44     }    
    45     else{ // 旋转方向与插入位置相反 
    46         int d = o.cmp(x);
    47         insert( o->ch[d], x );
    48         if( o->ch[d]->r > o->r )
    49             rotate( o, d^1 );
    50     }
    51 } 
    52 
    53 // 删除时,首先找到待删除节点.
    54 // 如果它只有一颗子树,情况就简单了.
    55 //         直接用这颗子树代替这个待删除节点成为根即可.(注意o是叶子节点也符合这个条件) 
    56 // 麻烦的是 o有两棵子树的情况.
    57 //        我们先把这两棵子树中优先级高的一颗旋转到根.
    58 //        然后递归在另一棵子树中删除节点o.
    59 //        比如,如果左子节点的优先级比较高,就必须右旋,否则会违反堆性质.
    60 //        如果右子节点优先级高,就必须左旋.代码如下.
    61 void remove( Node* &o, int x ){
    62     int d = o->cmp[x];
    63     if( d == -1 ){
    64         if( o->ch[0] == NULL ) o = o->ch[1];
    65         else if( o->ch[1] == NULL ) o = o->ch[0];
    66         else{
    67             int d2 = (o->ch[0]->r > o->ch[1]->r) ? 1 : 0; //旋转方向与优先级关系相反    
    68             retate( o, d2 );    remove( o->ch[d2], x );
    69         }    
    70     }    
    71     else    remove( o->ch[d], x );
    72 } 
    73 // 有意思的是,删除和插入是对称的. 
    74 // 事实上,前面的那个例子从前往后看是插入,从后往前看是删除.
    75 // 二者的共同特点是'只有一种旋转方法'
    76 
    77 // 上面的insert与remove 都没有处理
    78 // 待插入值已经存在, 待删除值不存在
    79 // 因此在调用相应函数之前请进行一次查找. 
    80 int find( Node *o, int x ){
    81     while( o != NULL ){
    82         int d = o->cmp(x);
    83         if( d == -1 ) return 1;
    84         else o = o->ch[d];    
    85     }    
    86     return 0;
    87 }
  • 相关阅读:
    Thrift中实现Java与Python的RPC互相调用
    Thrift介绍以及Java中使用Thrift实现RPC示例
    Netty中集成Protobuf实现Java对象数据传递
    ProtoBuf的介绍以及在Java中使用protobuf将对象进行序列化与反序列化
    ProtoBuf在使用protoc进行编译时提示: Required fields are not allowed in proto3
    Netty中使用WebSocket实现服务端与客户端的长连接通信发送消息
    Netty中实现多客户端连接与通信-以实现聊天室群聊功能为例(附代码下载)
    Netty的Socket编程详解-搭建服务端与客户端并进行数据传输
    Gradle项目在IDEA中运行时提示:Unnecessarily replacing a task that does not exist is not supported. Use create() or register() directly instead.
    Windows下curl的下载与使用
  • 原文地址:https://www.cnblogs.com/yefeng1627/p/3008773.html
Copyright © 2020-2023  润新知