一棵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语言中如何操作?

#include <stdio.h>
#include <stdlib.h>
#include <time.h> typedef struct node_t* Node;
typedef struct treap_t* Treap; struct node_t
{
Node left;//左节点
Node right;//右节点
int priority;//优先级
int key;//存储的关键字
}; struct treap_t
{
Node root;
}; //左旋转
void rotate_left(Node* node)
{
Node x = (*node)->right;
(*node)->right = x->left;
x->left = *node;
*node = x;
} //右旋转
void rotate_right(Node* node)
{
Node x = (*node)->left;
(*node)->left = x->right;
x->right = *node;
*node = x;
} //插入操作
void treap_insert(Node* root, int key, int priority)
{
//根为NULL,则直接创建此结点为根结点
if (*root == NULL)
{
*root = (Node)malloc(sizeof(struct node_t));
(*root)->left = NULL;
(*root)->right = NULL;
(*root)->priority = priority;
(*root)->key = key;
}
//向右插入结点
else if (key < (*root)->key)
{
treap_insert(&((*root)->left), key, priority);
if ((*root)->left->priority < (*root)->priority)
rotate_right(root);
}
//向左插入结点
else
{
treap_insert(&((*root)->right), key, priority);
if ((*root)->right->priority < (*root)->priority)
rotate_left(root);
}
} void treap_delete(Node* root, int key)
{
if (*root != NULL)
{
if (key < (*root)->key)
treap_delete(&((*root)->left), key);
else if (key > (*root)->key)
treap_delete(&((*root)->right), key);
else
{
//左右孩子都为空不用单独写出来
if ((*root)->left == NULL)
*root = (*root)->right;
else if ((*root)->right == NULL)
*root = (*root)->left;
else
{
//先旋转,然后再删除
if ((*root)->left->priority < (*root)->right->priority)
{
rotate_right(root);
treap_delete(&((*root)->right), key);
}
else
{
rotate_left(root);
treap_delete(&((*root)->left), key);
}
}
}
}
} //中序遍历
void in_order_traverse(Node root)
{
if (root != NULL)
{
in_order_traverse(root->left);
printf("%d\t", root->key);
in_order_traverse(root->right);
}
} //计算树的高度
int depth(Node node)
{
if(node == NULL)
return -1;
int l = depth(node->left);
int r = depth(node->right); return (l < r)?(r+1):(l+1);
} int main()
{
Treap treap = (Treap)malloc(sizeof(struct treap_t));
treap->root = NULL;
int i = 0; srand(time(0)); for (i = 0; i < 100; i++)
treap_insert(&(treap->root), i, rand());
in_order_traverse(treap->root);
printf("\n高度:%d\n", depth(treap->root)); printf("---分割线---\n"); for (i = 23; i < 59; i++)
treap_delete(&(treap->root), i);
in_order_traverse(treap->root);
printf("\n高度:%d\n", depth(treap->root));
return 0;
}

treap(树堆)的更多相关文章

  1. BZOJ3224/LOJ104 普通平衡树 treap(树堆)

    您需要写一种数据结构,来维护一些数,其中需要提供以下操作:1. 插入x2. 删除x(若有多个相同的数,因只删除一个)3. 查询x的排名(若有多个相同的数,因输出最小的排名)4. 查询排名为x的数5. ...

  2. 可旋转Treap(树堆)总结

    树堆,在数据结构中也称Treap,是指有一个随机附加域满足堆的性质的二叉搜索树,其结构相当于以随机数据插入的二叉搜索树.其基本操作的期望时间复杂度为O(logn).相对于其他的平衡二叉搜索树,Trea ...

  3. 查找——图文翔解Treap(树堆)

    之前我们讲到二叉搜索树,从二叉搜索树到2-3树到红黑树到B-树. 二叉搜索树的主要问题就是其结构与数据相关,树的深度可能会非常大,Treap树就是一种解决二叉搜索树可能深度过大的还有一种数据结构. T ...

  4. 树堆(Treap)学习笔记 2020.8.12

    如果一棵二叉排序树的节点插入的顺序是随机的,那么这样建立的二叉排序树在大多数情况下是平衡的,可以证明,其高度期望值为 \(O( \log_2 n )\).即使存在一些极端情况,但是这种情况发生的概率很 ...

  5. Treap树的基础知识

    原文 其它较好的的介绍:堆排序  AVL树 树堆,在数据结构中也称Treap(事实上在国内OI界常称为Traep,与之同理的还有"Tarjan神犇发明的"Spaly),是指有一个随 ...

  6. Treap树

    Treap树算是一种简单的优化策略,这名字大家也能猜到,树和堆的合体,其实原理比较简单,在树中维护一个"优先级“,”优先级“ 采用随机数的方法,但是”优先级“必须满足根堆的性质,当然是“大根 ...

  7. 6天通吃树结构—— 第三天 Treap树

    原文:6天通吃树结构-- 第三天 Treap树 我们知道,二叉查找树相对来说比较容易形成最坏的链表情况,所以前辈们想尽了各种优化策略,包括AVL,红黑,以及今天 要讲的Treap树. Treap树算是 ...

  8. Treap树 笔记

    预备知识:二叉查找树.堆(heap).平衡二叉树(AVL)的基本操作(左旋右旋) 定义: Treap.平衡二叉树.Tree+Heap.树堆. 每个结点两个键值(key.priority). 性质1. ...

  9. 真·浅谈treap树

    treap树是一种平衡树,它有平衡树的性质,满足堆的性质,是二叉搜索树,但是我们需要维护他 为什么满足堆的性质?因为每个节点还有一个随机权值,按照随机权值维持这个堆(树),可以用O(logn)的复杂度 ...

随机推荐

  1. php学习之mysqli的面向对象

    // mySqlTool.php  封装好的工具类 <?php class SqlTool{ private $conn; private $host="localhost" ...

  2. 『转载』Matlab中fmincon函数获取乘子

    Matlab中fmincon函数获取乘子 一.输出结构 [x,fval,exitflag,output,lambda] = fmincon(......) 二.结构说明 lambda结构 说     ...

  3. Python单元测试unittest - 单元测试框架

    一.unittest简介 unitest单元测试框架最初是有JUnit的启发,它支持测试自动化,共享测试的设置和关闭代码,将测试聚合到集合中,以及测试与报告框架的独立性. 二.unittest相关概念 ...

  4. tomcat参数调优

    在做java开发时尤其是大型软件开发时经常会遇到内存溢出的问题,比如说OutOfMemoryError等.这是个让开发人员很痛苦.也很纠结的问题,因为我们有时不知道什么样的操作导致了这种问题的发生.所 ...

  5. Android:Service

    Android Service: http://www.apkbus.com/android-15649-1-1.html android service 的各种用法(IPC.AIDL): http: ...

  6. Ex 5_28 Alice想要举办一个舞会..._第十次作业

    根据总人数建立顶点数量为总人数的无向图,顶点之间有边相连表示两个人相互认识,没有边则表示不认识.对于每一个顶点v,设d(v)表示顶点的度,若d(v)<5,即v认识的人数少于5,则不邀请v,若d( ...

  7. 各浏览器下使用 OBJECT 元素和 EMBED 元素嵌入 Flash 存在差异

    标准参考 OBJECT 元素定义了一个嵌入的对象.其引入的初衷是取代 IMG 和 APPLET 元素.不过由于安全等各方面原因以及缺乏浏览器支持,这一初衷并未实现.浏览器的对象支持依赖于对象类型.然而 ...

  8. C++ code:数值计算之矩形法求解积分问题

    积分的通常方法是将区域切割成一个个的小矩形,然后求这些小矩形的和.小矩形切割得越细,计算精度就越高,可以将切割小矩形的数量作为循环迭代变量,将前后两个不同精度下的小矩形和之差,作为逼近是否达到要求的比 ...

  9. python 全栈开发,Day101(redis操作,购物车,DRF解析器)

    昨日内容回顾 1. django请求生命周期? - 当用户在浏览器中输入url时,浏览器会生成请求头和请求体发给服务端 请求头和请求体中会包含浏览器的动作(action),这个动作通常为get或者po ...

  10. python 全栈开发,Day51(常用内置对象,函数,伪数组 arguments,关于DOM的事件操作,DOM介绍)

    昨日内容回顾 1.三种引入方式 1.行内js <div onclick = 'add(3,4)'></div> //声明一个函数 function add(a,b){ } 2. ...