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次。旋转次数为常数次。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值