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\t", 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("\n高度:%d\n", depth(treap->root)); 134 135 printf("---分割线---\n"); 136 137 for (i = 23; i < 59; i++) 138 treap_delete(&(treap->root), i); 139 in_order_traverse(treap->root); 140 printf("\n高度:%d\n", depth(treap->root)); 141 return 0; 142 }
复制代码习题:
B - 平衡树
Time Limit:10000MS     Memory Limit:131072KB     64bit IO Format:%lld & %llu
Submit Status Practice HYSBZ 3224

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\n",query_rank(root,x));break;
        case 4:printf("%d\n",query_num(root,x));break;
        case 5:ans=0;query_pro(root,x);printf("%d\n",tr[ans].v);break;
        case 6:ans=0;query_sub(root,x);printf("%d\n",tr[ans].v);break;
        }
    }
    return 0;
}

 

你可能感兴趣的:(Treap(树堆))