• Treap(树堆)


    treap是排序二叉树的一种改进,因为排序二叉树有可能会造成链状结构的时候复杂度变成O(n^2)所以通过随机一个优先级的方法来维持每次让优先级最大的作为树根,然后形成一个满足:

    A. 节点中的key满足排序二叉树(二叉查找树)

    B. 节点中的“优先级”满足大顶堆。

    可以证明通过这种方法维持的插入、删除、查找的期望时间复杂度为O(logn)

    一、节点的定义:左右孩子用指针数组的形式储存

    1 struct Node{
    2     Node*ch[2];//左右子树
    3     int r;//优先值
    4     int v;//
    5     int cmp(int x) const{
    6         if(x==v)return -1;
    7         return x<v?0:1;//0在ch[0]中正好是左孩子,对应向左搜索和操作,1在ch[1]中是右孩子,对应向右搜索和操作
    8     }
    9 };

    二、旋转:和大顶堆的调整方法类似,左旋和右旋,用于对优先级的堆排序,这里不会影响二叉排序树的性质,介绍一种位运算可以巧妙的进行左旋和右旋的选择——异或,相同为0,不同为1,那么x^1就是对x取反的操作。

    现在以下图为例

    o 表示的指向根节点的指针

    有旋转的代码如下:

     1 //左旋代码
     2 k = o->ch[0];
     3 o->ch[0] = k->ch[1];
     4 k->ch[1] = o;
     5 o = k;
     6 //右旋代码
     7 k = o->ch[1];
     8 o->ch[1] = k->ch[0];
     9 k->ch[0] = o;
    10 o = k;
    11 //两种代码可以写成一种,利用异或
    12 void rotate(Node* &o,int d){//d = 0 左旋,d = 1 右旋
    13     Node *k = o->ch[d^1];
    14     o->ch[d^1] = k->ch[d];
    15     k->ch[d] = o;
    16     o = k;
    17 }

     三、插入: 在插入的时候除了要在满足排序二叉树的插入要求,即递归的操作之外,还要满足堆的相应操作,所以,要通过旋转来实现,下面是代码:

     1 srand(time(NULL));
     2 void insert(Node *&o, int x){
     3     if(o==NULL){
     4         o = new Node();
     5         o->ch[0] = o->ch[1] = NULL;
     6         o->v = x;
     7         o->r = rand();
     8     }
     9     else {
    10         int d = o->cmp(x);//如果要插入的值x比当前根节点的值小则d==0,向左子树寻找,这里可以看出定义指针数组的好处
    11         insert(o->ch[d],x);
    12         if(o->ch[d]->r > o->r) rotate(o,d^1);//左孩子权值大右旋,右孩子权值大左旋
    13     }
    14 }

    四、删除:如果待删除结点为叶子结点或只有一棵子树,则用其子树(可能为空)替代它即可。如果有两棵子树,则将孩子中优先级高的旋转到根,然后在另一棵子树中递归删除目标点。

    代码如下:

     1 void remove(Node *&o,int x){
     2     int d = o->cmp(x);
     3     if(d==-1){//找到了
     4         if(o->ch[0] == NULL) o = o->ch[1];
     5         else if(o->ch[1] == NULL) o = o->ch[0];
     6         else {
     7             int d2 = ((o->ch[0]->r)>(o->ch[1]->r)?1:0);//删除节点的左孩子权值大右旋,右孩子权值大左旋
     8             rotate(o,d2);
     9             remove(o->ch[d2],x);
    10         }
    11     }
    12     else remove(o->ch[d],x);
    13 }

    五、查找:在插入和查找前进行查找,防止特殊情况,代码如下

    1 int find(Node*o, int x){
    2     while(o!=NULL){
    3         int d = o->cmp(x);
    4         if(d==-1) return 1;//找到了
    5         else o = o->ch[d];
    6     }
    7     return 0;//不存在
    8 }

     六、应用,这个数据结构可以用来解决二叉排序树的超时问题

  • 相关阅读:
    最短路径算法 2.Dijkstra算法
    最短路径算法 1.Floyed-Warshall算法
    POLYGON(动态规划)
    JAG Asia 2016-Similarity of Subtrees(BFS+哈希)
    2019ACM-ICPC南京网络赛Holy Grail (SPFA / Floyd 模板题)
    2019ACM-ICPC南京网络赛Greedy Sequence
    快速输入输出
    2019HDU多校训练第七场J-Just Repeat
    JAG Asia 2017 C-----Prime-Factor Prime(素数区间筛)
    2019HDU多校训练第五场1007-permutation 2
  • 原文地址:https://www.cnblogs.com/shanyr/p/5200819.html
Copyright © 2020-2023  润新知