数据结构之Treap详解_C 语言

1. 概述

同splay tree一样,treap也是一个平衡二叉树,不过Treap会记录一个额外的数据,即优先级。Treap在以关键码构成二叉搜索树的同时,还按优先级来满足堆的性质。因而,Treap=tree+heap。这里需要注意的是,Treap并不是二叉堆,二叉堆必须是完全二叉树,而Treap可以并不一定是。

2. Treap基本操作

为了使Treap 中的节点同时满足BST性质和最小堆性质,不可避免地要对其结构进行调整,调整方式被称为旋转。在维护Treap 的过程中,只有两种旋转,分别是左旋转(简称左旋)和右旋转(简称右旋)。
左旋一个子树,会把它的根节点旋转到根的左子树位置,同时根节点的右子节点成为子树的根;右旋一个子树,会把它的根节点旋转到根的右子树位置,同时根节点的左子节点成为子树的根。

struct Treap_Node

{

 Treap_Node *left,*right; //节点的左右子树的指针

 int value,fix; //节点的值和优先级

};

void Treap_Left_Rotate(Treap_Node *&a) //左旋 节点指针一定要传递引用

{

 Treap_Node *b=a->right;

 a->right=b->left;

 b->left=a;

 a=b;

}

void Treap_Right_Rotate(Treap_Node *&a) //右旋 节点指针一定要传递引用

{

 Treap_Node *b=a->left;

 a->left=b->right;

 b->right=a;

 a=b;

}

3. Treap的操作

同其他树形结构一样,treap的基本操作有:查找,插入,删除等。

3.1    查找

同其他二叉树一样,treap的查找过程就是二分查找的过程,复杂度为O(lg n)。

3.2    插入

在Treap 中插入元素,与在BST 中插入方法相似。首先找到合适的插入位置,然后建立新的节点,存储元素。但是要注意新的节点会有一个优先级属性,该值可能会破坏堆序,因此我们要根据需要进行恰当的旋转。具体方法如下:

1. 从根节点开始插入;
2. 如果要插入的值小于等于当前节点的值,在当前节点的左子树中插入,插入后如果左子节点的优先级小于当前节点的优先级,对当前节点进行右旋;
3. 如果要插入的值大于当前节点的值,在当前节点的右子树中插入,插入后如果右子节点的优先级小于当前节点的优先级,对当前节点进行左旋;
4. 如果当前节点为空节点,在此建立新的节点,该节点的值为要插入的值,左右子树为空,插入成功。

Treap_Node *root;

void Treap_Insert(Treap_Node *&P,int value) //节点指针一定要传递引用

{

 if (!P) //找到位置,建立节点

 {

  P=new Treap_Node;

  P->value=value;

  P->fix=rand();//生成随机的修正值

 }

 else if (value <= P->value)

 {

  Treap_Insert(P->left,r);

  if (P->left->fix < P->fix)

   Treap_Right_Rotate(P);//左子节点修正值小于当前节点修正值,右旋当前节点

 }

 else

 {

  Treap_Insert(P->right,r);

  if (P->right->fix < P->fix)

   Treap_Left_Rotate(P);//右子节点修正值小于当前节点修正值,左旋当前节点

 }

}

3.3   删除

与BST 一样,在Treap 中删除元素要考虑多种情况。我们可以按照在BST 中删除元素同样的方法来删除Treap 中的元素,即用它的后继(或前驱)节点的值代替它,然后删除它的后继(或前驱)节点。

上述方法期望时间复杂度为O(logN),但是这种方法并没有充分利用Treap 已有的随机性质,而是重新得随机选取代替节点。我们给出一种更为通用的删除方法,这种方法是基于旋转调整的。首先要在Treap 树中找到待删除节点的位置,然后分情况讨论:

情况一,该节点为叶节点或链节点,则该节点是可以直接删除的节点。若该节点有非空子节点,用非空子节点代替该节点的,否则用空节点代替该节点,然后删除该节点。

情况二,该节点有两个非空子节点。我们的策略是通过旋转,使该节点变为可以直接删除的节点。如果该节点的左子节点的优先级小于右子节点的优先级,右旋该节点,使该节点降为右子树的根节点,然后访问右子树的根节点,继续讨论;反之,左旋该节点,使该节点降为左子树的根节点,然后访问左子树的根节点,这样继续下去,直到变成可以直接删除的节点。

BST_Node *root;

void Treap_Delete(Treap_Node *&P,int *value) //节点指针要传递引用

{

 if (value==P->value) //找到要删除的节点 对其删除

 {

  if (!P->right || !P->left) //情况一,该节点可以直接被删除

  {

   Treap_Node *t=P;

   if (!P->right)

    P=P->left; //用左子节点代替它

   else

    P=P->right; //用右子节点代替它

   delete t; //删除该节点

  }

  else //情况二

  {

   if (P->left->fix < P->right->fix) //左子节点修正值较小,右旋

   {

    Treap_Right_Rotate(P);

    Treap_Delete(P->right,r);

   }

   else //左子节点修正值较小,左旋

   {

    Treap_Left_Rotate(P);

     Treap_Delete(P->left,r);

   }

  }

 }

 else if (value < P->value)

  Treap_Delete(P->left,r); //在左子树查找要删除的节点

 else

  Treap_Delete(P->right,r); //在右子树查找要删除的节点

}

4. Treap应用
Treap可以解决splay tree可以解决的所有问题,具体参见另一篇文章:《数据结构之伸展树详解》

可以这样定义结构体:

struct Treap_Node

{

 Treap_Node *left,*right; //节点的左右子树的指针

 int value,fix,weight,size; //节点的值,优先级,重复计数(记录相同节点个数,节省空间),子树大小

 inline int lsize(){ return left ?left->size ?0; } //返回左子树的节点个数

 inline int rsize(){ return right?right->size?0; } //返回右子树的节点个数

};

5. 总结

Treap 作为一种简洁高效的有序数据结构,在计算机科学和技术应用中有着重要的地位。它可以用来实现集合、多重集合、字典等容器型数据结构,也可以用来设计动态统计数据结构。

6. 参考资料

(1)Treap:http://www.nocow.cn/index.php/Treap
(2)随机平衡二叉查找树Treap 的分析与应用:http://www.byvoid.com/blog/wp-content/uploads/2010/12/treap-analysis-and-application.pdf

以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索数据结构
Treap
treap详解、c语言结构体详解、c语言file结构体详解、数据结构链表详解、数据结构详解,以便于您获取更多的相关知识。

时间: 2024-10-05 03:13:16

数据结构之Treap详解_C 语言的相关文章

数据结构之堆详解_C 语言

1. 概述 堆(也叫优先队列),是一棵完全二叉树,它的特点是父节点的值大于(小于)两个子节点的值(分别称为大顶堆和小顶堆).它常用于管理算法执行过程中的信息,应用场景包括堆排序,优先队列等. 2. 堆的基本操作 堆是一棵完全二叉树,高度为O(lg n),其基本操作至多与树的高度成正比.在介绍堆的基本操作之前,先介绍几个基本术语: A:用于表示堆的数组,下标从1开始,一直到n PARENT(t):节点t的父节点,即floor(t/2) RIGHT(t):节点t的左孩子节点,即:2*t LEFT(t

C语言 经典题目螺旋矩阵 实例详解_C 语言

C语言 经典题目螺旋矩阵 //N阶螺旋矩阵 #include <stdio.h> #include <stdlib.h> int main() { int N,i,j,n,num=1; int a[10][10]={0}; printf("输入你要输出的几阶中断:"); scanf("%d",&N); for(n=0;n<=N/2;n++) { for(j=n;j<=N-n-1;j++) a[n][j]=num++; fo

数据结构之伸展树详解_C 语言

1. 概述 二叉查找树(Binary Search Tree,也叫二叉排序树,即Binary Sort Tree)能够支持多种动态集合操作,它可以用来表示有序集合.建立索引等,因而在实际应用中,二叉排序树是一种非常重要的数据结构. 从算法复杂度角度考虑,我们知道,作用于二叉查找树上的基本操作(如查找,插入等)的时间复杂度与树的高度成正比.对一个含n个节点的完全二叉树,这些操作的最坏情况运行时间为O(log n).但如果因为频繁的删除和插入操作,导致树退化成一个n个节点的线性链(此时即为一个单链表

C迷途指针详解_C 语言

本文较为详尽的讲述了C语言的迷途指针,分析了其概念.原理与检测方法.分享给大家供大家参考.具体如下: 一般来说,在计算机编程领域中,迷途指针,或称悬空指针.野指针,指的是不指向任何合法的对象的指针. 当所指向的对象被释放或者收回,但是对该指针没有作任何的修改,以至于该指针仍旧指向已经回收的内存地址,此情况下该指针便称迷途指针.若操作系统将这部分已经释放的内存重新分配给另外一个进程,而原来的程序重新引用现在的迷途指针,则将产生无法预料的后果.因为此时迷途指针所指向的内存现在包含的已经完全是不同的数

C语言中操作进程信号的相关函数使用详解_C 语言

C语言signal()函数:设置信号处理方式头文件: #include <signal.h> 定义函数: void (*signal(int signum, void(* handler)(int)))(int); 函数说明:signal()会依参数signum 指定的信号编号来设置该信号的处理函数. 当指定的信号到达时就会跳转到参数handler 指定的函数执行. 如果参数handler 不是函数指针, 则必须是下列两个常数之一: 1.SIG_IGN 忽略参数signum 指定的信号. 2.

C++计数排序详解_C 语言

计数排序不同于比较排序,是基于计数的方式,对于计数排序,假设每一个输入都是介于0~k之间的整数.对于每一个输入元素x,确定出小于x的元素的个数.假如有17个元素小于x,则x就属于第18个输出位置. 计数排序涉及到三个数组A[0-..length-1],length为数组A的长度:数组B与数组A长度相等,存放最终排序的结果:C[0-..K]存放A中每个元素的个数,k为数组A中的最大值. int count_k(int A[],int length),此函数为了确定数组A中最大的元素,用来确定C数组

C++的get()函数与getline()函数使用详解_C 语言

C++ get()函数读入一个字符 get()函数是cin输入流对象的成员函数,它有3种形式:无参数的,有一个参数的,有3个参数的. 1) 不带参数的get函数 其调用形式为 cin.get() 用来从指定的输入流中提取一个字符(包括空白字符),函数的返回值就是读入的字符. 若遇到输入流中的文件结束符,则函数值返回文件结束标志EOF(End Of File),一般以-1代表EOF,用-1而不用0或正值,是考虑到不与字符的ASCII代码混淆,但不同的C ++系统所用的EOF值有可能不同. [例]

C++编程中的格式化输出详解_C 语言

在输出数据时,为简便起见,往往不指定输出的格式,由系统根据数据的类型采取默认的格式,但有时希望数据按指定的格式输出,如要求以十六进制或八进制形式输出一个 整数,对输出的小数只保留两位小数等.有两种方法可以达到此目的.一种是使用控制符的方法:第2种是使用流对象的有关成员函数.分别叙述如下. 使用控制符控制输出格式 控制格式的使用方法这里不再赘述,仅举例说明 [例] 用控制符控制输出格式. #include <iostream> #include <iomanip>//不要忘记包含此头

C++运算符重载规则详解_C 语言

C++允许重载的运算符和不允许重载的运算符 C++中绝大部分的运算符允许重载,具体规定见表 不能重载的运算符只有5个: .  (成员访问运算符) .*  (成员指针访问运算符) ::  (域运算符) sizeof  (长度运算符) ?:  (条件运算符) 前两个运算符不能重载是为了保证访问成员的功能不能被改变,域运算符和sizeof 运算符的运算对象是类型而不是变量或一般表达式,不具备重载的特征. C++运算符重载的规则 C++对运算符重载定义了如下几条规则. 1) C++不允许用户自己定义新的