zoukankan      html  css  js  c++  java
  • Treap(树堆)(转)

      Treap是个有着跟AVL树类似功能的数据结构,而且写起来更为方便。

      转自:Microgoogle(http://www.cnblogs.com/shuaiwhu/archive/2012/05/06/2485894.html)

    一棵treap是一棵修改了结点顺序的二叉查找树,如图,显示一个例子,通常树内的每个结点x都有一个关键字值key[x],另外,还要为结点分配priority[x],它是一个独立选取的随机数。
    假设所有的优先级是不同的,所有的关键字也是不同的。treap的结点排列成让关键字遵循二叉查找树性质,并且优先级遵循最小堆顺序性质:
    1.如果v是u的左孩子,则key[v] < key[u].
    2.如果v是u的右孩子,则key[v] > key[u].
    3.如果v是u的孩子,则priority[u] > priority[u].
    这两个性质的结合就是为什么这种树被称为“treap”的原因,因为它同时具有二叉查找树和堆的特征。


    用以下方式考虑treap会有帮助。假设插入关联关键字的结点x1,x2,...,xn到一棵treap内。结果的treap是将这些结点 以它们的优先级(随机选取)的顺序插入一棵正常的二叉查找树形成的,亦即priority[xi] < priority[xj]表示xi在xj之前被插入。
    在算法导论的12.4节中,其证明了随机构造的二叉查找树的期望高度为O(lgn),因而treap的期望高度亦是O(lgn)。

    treap插入操作:
    1.按照二叉树的插入方法,将结点插入到树中
    2.根据堆的性质(我们这里为最小堆)和优先级的大小调整结点位置。

    treap删除操作:
    1.找到相应的结点
    2.若该结点为叶子结点,则直接删除;
    若该结点为只包含一个叶子结点的结点,则将其叶子结点赋值给它;
    若该结点为其他情况下的节点,则进行相应的旋转,直到该结点为上述情况之一,然后进行删除。

    旋转主要涉及到右旋转的左旋转,下面把右旋转的图画在下面:

    代码如下:(已通过GCC和VC编译)

    PS:请教一下大家,在C语言中是没有引用的,因而在treap_insert(Node* root, int key, int priority)函数中(第40行),由于root要跟着改变,因而必须传root地址,即&root(第131行),因而导致在写代码时,显 得很不好看,如传root的left的地址为参数,必须写成&((*root)->left)(第72行)。如果用C++写,直接用引用, 则代码看起来简洁很多,不知在C语言中如何操作?

      1 #include <stdio.h>
      2 #include <stdlib.h>
      3 #include <time.h>
      4 
      5 typedef struct node_t* Node;
      6 typedef struct treap_t* Treap;
      7 
      8 struct node_t
      9 {
     10   Node left;//左节点
     11   Node right;//右节点
     12   int priority;//优先级
     13   int key;//存储的关键字
     14 };
     15 
     16 struct treap_t
     17 {
     18   Node root;
     19 };
     20 
     21 //左旋转
     22 void rotate_left(Node* node)
     23 {
     24   Node x = (*node)->right;
     25   (*node)->right = x->left;
     26   x->left = *node;
     27   *node = x;
     28 }
     29 
     30 //右旋转
     31 void rotate_right(Node* node)
     32 {
     33   Node x = (*node)->left;
     34   (*node)->left = x->right;
     35   x->right = *node;
     36   *node = x;
     37 }
     38 
     39 //插入操作
     40 void treap_insert(Node* root, int key, int priority)
     41 {
     42   //根为NULL,则直接创建此结点为根结点
     43   if (*root == NULL)
     44   {
     45     *root = (Node)malloc(sizeof(struct node_t));
     46     (*root)->left = NULL;
     47     (*root)->right = NULL;
     48     (*root)->priority = priority;
     49     (*root)->key = key;
     50   }
     51   //向左插入结点
     52   else if (key < (*root)->key)
     53   {
     54     treap_insert(&((*root)->left), key, priority);
     55     if ((*root)->left->priority < (*root)->priority)
     56       rotate_right(root);
     57   }
     58   //向右插入结点
     59   else
     60   {
     61     treap_insert(&((*root)->right), key, priority);
     62     if ((*root)->right->priority < (*root)->priority)
     63       rotate_left(root);
     64   }
     65 }
     66 
     67 void treap_delete(Node* root, int key)
     68 {
     69   if (*root != NULL)
     70   {
     71     if (key < (*root)->key)
     72       treap_delete(&((*root)->left), key);
     73     else if (key > (*root)->key)
     74       treap_delete(&((*root)->right), key);
     75     else
     76     {
     77       //左右孩子都为空不用单独写出来
     78       if ((*root)->left == NULL)
     79         *root = (*root)->right;
     80       else if ((*root)->right == NULL)
     81         *root = (*root)->left;
     82       else
     83       {
     84         //先旋转,然后再删除
     85         if ((*root)->left->priority < (*root)->right->priority)
     86         {
     87           rotate_right(root);
     88           treap_delete(&((*root)->right), key);
     89         }
     90         else
     91         {
     92           rotate_left(root);
     93           treap_delete(&((*root)->left), key);
     94         }
     95       }
     96     }
     97   }
     98 }
     99 
    100 //中序遍历
    101 void in_order_traverse(Node root)
    102 {
    103   if (root != NULL)
    104   {
    105     in_order_traverse(root->left);
    106     printf("%d	", root->key);
    107     in_order_traverse(root->right);
    108   }
    109 }
    110 
    111 //计算树的高度
    112 int depth(Node node)
    113 {
    114     if(node == NULL)
    115         return -1;
    116     int l = depth(node->left);
    117     int r = depth(node->right);
    118 
    119     return (l < r)?(r+1):(l+1);
    120 }
    121 
    122 int main()
    123 {
    124   Treap treap = (Treap)malloc(sizeof(struct treap_t));
    125   treap->root = NULL;
    126   int i = 0;
    127   
    128   srand(time(0));
    129   
    130   for (i = 0; i < 100; i++)
    131     treap_insert(&(treap->root), i, rand());
    132   in_order_traverse(treap->root);
    133   printf("
    高度:%d
    ", depth(treap->root));
    134   
    135   printf("---分割线---
    ");
    136 
    137   for (i = 23; i < 59; i++)
    138     treap_delete(&(treap->root), i);
    139   in_order_traverse(treap->root);
    140   printf("
    高度:%d
    ", depth(treap->root));
    141   return 0;
    142 }
    B - 平衡树
    Time Limit:10000MS     Memory Limit:131072KB     64bit IO Format:%lld & %llu

    Description

    您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作:
    1. 插入x数
    2. 删除x数(若有多个相同的数,因只删除一个)
    3. 查询x数的排名(若有多个相同的数,因输出最小的排名)
    4. 查询排名为x的数
    5. 求x的前驱(前驱定义为小于x,且最大的数)
    6. 求x的后继(后继定义为大于x,且最小的数)

    Input

    第一行为n,表示操作的个数,下面n行每行有两个数opt和x,opt表示操作的序号(1<=opt<=6)

    Output

    对于操作3,4,5,6每行输出一个数,表示对应答案

    Sample Input

    10
    1 106465
    4 1
    1 317721
    1 460929
    1 644985
    1 84185
    1 89851
    6 81968
    1 492737
    5 493598

    Sample Output

    106465
    84185
    492737

    Hint

    1.n的数据范围:n<=100000

    2.每个数的数据范围:[-1e7,1e7]
    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    using namespace std;
    struct data{
        int l,r,v,size,rnd,w;
    }tr[100005];
    int n,size,root,ans;
    void update(int k)//更新结点信息
    {
        tr[k].size=tr[tr[k].l].size+tr[tr[k].r].size+tr[k].w;
    }
    void rturn(int &k)
    {
        int t=tr[k].l;tr[k].l=tr[t].r;tr[t].r=k;
        tr[t].size=tr[k].size;update(k);k=t;
    }
    void lturn(int &k)
    {
        int t=tr[k].r;tr[k].r=tr[t].l;tr[t].l=k;
        tr[t].size=tr[k].size;update(k);k=t;
    }
    void insert(int &k,int x)
    {
        if(k==0)
        {
            size++;k=size;
            tr[k].size=tr[k].w=1;tr[k].v=x;tr[k].rnd=rand();
            return;
        }
        tr[k].size++;
        if(tr[k].v==x)tr[k].w++;//每个结点顺便记录下与该节点相同值的数的个数
        else if(x>tr[k].v)
        {
            insert(tr[k].r,x);
            if(tr[tr[k].r].rnd<tr[k].rnd)lturn(k);//维护堆性质
        }
        else 
        {
            insert(tr[k].l,x);
            if(tr[tr[k].l].rnd<tr[k].rnd)rturn(k);
        } 
    }
    void del(int &k,int x)
    {
        if(k==0)return; 
        if(tr[k].v==x)
        {
            if(tr[k].w>1)
            {
                tr[k].w--;tr[k].size--;return;//若不止相同值的个数有多个,删去一个
            }
            if(tr[k].l*tr[k].r==0)k=tr[k].l+tr[k].r;//有一个儿子为空
            else if(tr[tr[k].l].rnd<tr[tr[k].r].rnd)
                rturn(k),del(k,x);
            else lturn(k),del(k,x);
        }
        else if(x>tr[k].v)
            tr[k].size--,del(tr[k].r,x);
        else tr[k].size--,del(tr[k].l,x);
    }
    int query_rank(int k,int x)
    {
        if(k==0)return 0;
        if(tr[k].v==x)return tr[tr[k].l].size+1;
        else if(x>tr[k].v)
            return tr[tr[k].l].size+tr[k].w+query_rank(tr[k].r,x);
        else return query_rank(tr[k].l,x);
    }
    int query_num(int k,int x)
    {
        if(k==0)return 0;
        if(x<=tr[tr[k].l].size)
            return query_num(tr[k].l,x);
        else if(x>tr[tr[k].l].size+tr[k].w)
            return query_num(tr[k].r,x-tr[tr[k].l].size-tr[k].w);
        else return tr[k].v;
    }
    void query_pro(int k,int x)
    {
        if(k==0)return;
        if(tr[k].v<x)
        {
            ans=k;query_pro(tr[k].r,x);
        }
        else query_pro(tr[k].l,x);
    }
    void query_sub(int k,int x)
    {
        if(k==0)return;
        if(tr[k].v>x)
        {
            ans=k;query_sub(tr[k].l,x);
        }
        else query_sub(tr[k].r,x);
    }
    int main()
    {
        scanf("%d",&n);
        int opt,x;
        for(int i=1;i<=n;i++)
        {
            scanf("%d%d",&opt,&x);
            switch(opt)
            {
            case 1:insert(root,x);break;
            case 2:del(root,x);break;
            case 3:printf("%d
    ",query_rank(root,x));break;
            case 4:printf("%d
    ",query_num(root,x));break;
            case 5:ans=0;query_pro(root,x);printf("%d
    ",tr[ans].v);break;
            case 6:ans=0;query_sub(root,x);printf("%d
    ",tr[ans].v);break;
            }
        }
        return 0;
    }
  • 相关阅读:
    65 进程互斥锁的优化实现
    Linux多线程编程
    互斥锁和自旋锁
    64 进程互斥锁的初步实现(下)
    63 进程互斥锁的初步实现(中)
    Linux中断子系统
    62 进程互斥锁的初步实现(上)
    61 进程互斥锁的详细设计
    Linux进程调度的时机
    嵌入式领域linux作为实时操作系统的缺点(转)
  • 原文地址:https://www.cnblogs.com/topW2W/p/5218693.html
Copyright © 2011-2022 走看看