Treap树

(a) ①根据堆性质以每个结点为根的子树的最小优先级都是在子树的根上。②根据二叉搜索树性质,以每个节点的根的子树的左子树<右子树。

根据这两点根和左右子树都是被唯一确定,因此能够确定整棵树。

(b)Treap树是二叉查找树的一种,而二叉查找树期望高度为O(lgn),所以treap期望高度为O(lgn).

(c)代码如下:

//13-4 Treap树
#include <iostream>
#include <time.h>
using namespace std;
#define LEN sizeof(struct Treap)
#define n 100//树中元素个数。
struct Treap
{
	char key;
	int priority;
    struct Treap*lchild;
    struct Treap*rchild;
	struct Treap*parent;
};
struct Treap*root=NULL;
int RAND(int a[],int i)//随机选择N个互不相同的数。
{
	int k=rand()%n+1;
	for (int j=0;j<i;j++)
	{
		if (a[j]==k)
		{
			k=rand()%n+1;
			j=-1;
		}
	}
	return k;
}
struct Treap*LEFT_ROTATE(struct Treap*x)
{//左旋转:分三个步骤①②③来叙述旋转代码的。
	struct Treap*y=x->rchild;//设置y结点。
	x->rchild=y->lchild;//本行代码以及下面的if结构表达的是“y的左孩子成为x的右孩子”。①
	if(y->lchild!=NULL)
	{
		y->lchild->parent=x;
	}
	y->parent=x->parent;//本行代码以及下面的if-else结构表达的过程是“y成为该子树新的根”。②
	if(x->parent==NULL)
	{
		root=y;
	}
	else if(x==x->parent->lchild)
	{
		x->parent->lchild=y;
	}
	else x->parent->rchild=y;
	y->lchild=x;//本行代码以及下面一行都表达了“x成为y的左孩子”。③
	x->parent=y;
	return y;
}
struct Treap* RIGHT_ROTATE(struct Treap*x)
{//右旋转:分三个步骤①②③来叙述旋转代码的。
	struct Treap*y=x->lchild;//设置y结点。
	x->lchild=y->rchild;//本行代码以及下面的if结构表达的是“y的左孩子成为x的右孩子”。①
	if(y->rchild!=NULL)
	{
		y->rchild->parent=x;
	}
	y->parent=x->parent;//本行代码以及下面的if-else结构表达的过程是“y成为该子树新的根”。②
	if(x->parent==NULL)
	{
		root=y;
	}
	else if(x==x->parent->rchild)
	{
		x->parent->rchild=y;
	}
	else x->parent->lchild=y;
	y->rchild=x;//本行代码以及下面一行都表达了“x成为y的左孩子”。③
	x->parent=y;
	// 结点的位置变了, 要更新结点的高度值
	return y;
}
//非递归的插入函数
void ITERATIVE_TREE_INSERT(struct Treap*&root,struct Treap*z)
{
	struct Treap*y=NULL;
	struct Treap*x=root;
	while (x)
	{
		y=x;
		if (z->key<x->key)
			x=x->lchild;
		else x=x->rchild;
	}
	z->parent=y;
	if (y==NULL)
	{
		root=z;
	}
	else if(z->key<y->key)
	{
		y->lchild=z;
	}
	else y->rchild=z;
    z->lchild=z->rchild=NULL;
	struct Treap*p=y;
	while (z->parent!=NULL&&z->priority<z->parent->priority)
	{//和AVL树类似,从新插入结点往上查询满足条件的结点做旋转,直到满足Treap树。
		if (z->parent->lchild==z)//z是左孩子
		{
			p=RIGHT_ROTATE(p);
		}
		else//z是右孩子
		{
			p=LEFT_ROTATE(p);
		}
		p=p->parent;
	}
}
//中序遍历
void InOderTraverse(struct Treap *p)
{
    if (p)
	{
		InOderTraverse(p->lchild);
		cout<<p->key<<" "<<p->priority<<endl;
		InOderTraverse(p->rchild);
	}
}
void main()
{
	srand( (unsigned)time( NULL ) );
	int i=0;
	char B[10]={'G','B','H','A','E','K','I','C','D','F'};
	int a[10]={0};
	while (i!=10)
	{
		struct Treap*z=new struct Treap[LEN];
		z->key=B[i];
		z->priority=RAND(a,i);
		a[i++]=z->priority;
		ITERATIVE_TREE_INSERT(root,z);
	}
    InOderTraverse(root);
}

(d)正如题目中所说的。Treap_insert先执行一个查找然后做一些列旋转。查找时是从树根一直向下移动到叶子结点的一条简单路径上。而旋转则是从新插入结点往上移动到根结点,所以两者都要进行O(h)次迭代或者递归操作,其中h=O(lgn)(根据b部分),所以Treap_insert操作需要O(lgn)期望时间。

(e)根据Treap_insert函数的最后旋转循环知:若新结点z是其父的左孩子(x是z的右子树),则进行右旋转,右旋转是以左脊柱为轴进行的,旋转次数为D。若z是其父的右孩子(x是z的左子树),则以右脊柱为轴做左旋转。旋转次数C注意这里的z和题目中的x不一样。总旋转次数=C+D。

(f)若y是x左子树的右脊柱:

如图可知:根据堆的性质,y的优先级大于x优先级。根据二叉查找树性质,key[y]<key[x],而对于z,y的优先级小于z的优先级。其他情况都不满足y是x左子树的右脊柱的前提。

(g) 从关键字k到i有k-i+1个数据,也就是有(k-i+1)!种二叉查找树可能的结构。其中满足y是x左子树的右脊柱的结构有(k-i+1-2)!种,其中k和k的左孩子是位置固定不变的。那么k到i中满足f的概率就是g所给的答案。数学简化过程略。

(h)此处用到了期望值的定义。i有1到k-1个取值可能,所以对于这k-1种可能中的每种可能都有发生f部分的情况。这些情况做个期望∑就得到了期望值E(C).

(i)根据对称性,若y是x右子树的左脊柱,那么g的概率分析可以拿来直接用,而根据h.  i的取值范围变成了1到n-k,有n-k种取值,分析过程和h类似。

(j)旋转次数=E(C)+E(D)=2-1/k-1/(n-k+1)<2,所以期望次数小于2次。旋转次数为常数次。



Treap树,布布扣,bubuko.com

时间: 2024-10-19 11:08:22

Treap树的相关文章

bzoj2141 树状数组套Treap树

题目大意是在能够改变两个数的位置的情况下计算逆序对数 这因为是动态记录逆序对 本来单纯逆序对只要用树状数组计算即可,但这里因为更新,所以利用TReap树的删点和增加点来进行更新 大致是把每个树状数组所管理的点都放在对应的Treap树下, 这样x-=lowbit(x)下来,正好访问到是所有比他小范围下的点了 然后根据每棵访问到的Treap树有多少个节点是比当前值小的即可 每次交换ai , aj , (i<j)只要考虑(i,j)范围内比ai大的数,比aj小的数,然后加加减减即可 如果ai!=aj也是

Noi2004郁闷的出纳员-treap树

treap树只需要单旋,可以写在一个函数中.当需要删除某点时只需不断将这个点下旋知道它只有少于一个的儿子,让他的 儿子 或者 空 取代它.插入时先插入,然后从底下向上通过旋转维护堆的性质.下面附标程: 1 #include<iostream> 2 #include<cstdlib> 3 #include<cstdio> 4 #include<algorithm> 5 using namespace std; 6 struct Node{ 7 int ch[2

treap树模板

1 ///treap树模板 2 typedef struct Node ///节点的结构体 3 { 4 Node *l,*r; 5 int val,pri; ///节点的值和优先级 6 int sz; ///节点子树的节点数 7 Node(int x) ///初始化节点 8 { 9 l=r=NULL; 10 val=x; 11 pri=rand(); 12 sz=1; 13 } 14 }Node; 15 Node *root; 16 17 int Tsize(Node *T) ///计算子树的叶

poj 1442 Black Box (treap树入门题)

1 /**************************************************** 2 题目: Black Box(poj 1442) 3 链接: http://poj.org/problem?id=1442 4 题意: 给n个数,m个询问,对第i数个询问前Xi个数中第 5 i小的是那个数. 6 算法: treap树 7 *****************************************************/ 8 #include<iostream

POJ 1442 Black Box(treap树)

题目链接:点击打开链接 思路:treap树模板题, 可以动态维护一个有序表, 支持在O(logN)的时间内完成插入.删除一个元素和查找第K大元素的任务. 当然, treap树能做到的还远远不止这些, 常常与其他数据结构嵌套. treap树是一种平衡二叉搜索树, 既满足堆的条件, 又满足排序二叉树的条件. 细节参见代码: #include <cstdio> #include <cstring> #include <algorithm> #include <iostr

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

原文:6天通吃树结构-- 第三天 Treap树 我们知道,二叉查找树相对来说比较容易形成最坏的链表情况,所以前辈们想尽了各种优化策略,包括AVL,红黑,以及今天 要讲的Treap树. Treap树算是一种简单的优化策略,这名字大家也能猜到,树和堆的合体,其实原理比较简单,在树中维护一个"优先级“,”优先级“ 采用随机数的方法,但是”优先级“必须满足根堆的性质,当然是“大根堆”或者“小根堆”都无所谓,比如下面的一棵树: 从树中我们可以看到: ①:节点中的key满足“二叉查找树”. ②:节点中的“优

URAL 1439. Battle with You-Know-Who treap树

题目来源:URAL 1439. Battle with You-Know-Who 题意:开始有数列1, 2, 3, ... L k输出第k大的数 D k删除第k大的数 思路:treap树插入删除的数 每次二分查找第k大的数为mid 查询treap小于等于mid的数有y个 那么mid应该是第mid-y大的数 与k比较 继续二分 #include <cstdio> #include <cstring> #include <cstdlib> #include <algo

Treap树模板hdu-4585

目录 例题:hdu 4585 Treap树 1.Treap树的唯一性 2.Treap树的平衡问题 3.Treap树的数据结构 4.Treap树的插入 5.插入过程中维护堆的过程中用到的旋转 6.寻找第k大的数 O(logn) 7.查询某个数的名次 O(logn) 8.hdu 4585 AC代码 例题:hdu 4585 Treap树 是一种简单的平衡二叉搜索树. 二叉搜索树的每一个节点都有一个键值,除此之外Treap树为每个节点人为添加了一个称之为优先级的权值.对于键值来说,这是一棵二叉搜索树,对

Treap树的基础知识

原文 其它较好的的介绍:堆排序  AVL树 树堆,在数据结构中也称Treap(事实上在国内OI界常称为Traep,与之同理的还有"Tarjan神犇发明的"Spaly),是指有一个随机附加域满足堆的性质的二叉搜索树,其结构相当于以随机数据插入的二叉搜索树.其基本操作的期望时间复杂度为O(logn).相对于其他的平衡二叉搜索树,Treap的特点是实现简单,且能基本实现随机平衡的结构. 编辑 我们可以看到,如果一个二叉排序树节点插入的顺序是随机的,这样我们得到的二叉排序树大多数情况下是平衡的