Treap 学习整理

Treap(树堆)

AVL:依靠左右节点的深度控制平衡
红黑树:依靠链中黑节点数相同和红节点不可相邻控制平衡
SBT:依靠左右节点和对应节点的子节点的大小关系控制平衡
TREAP:树堆依靠随机数实现平衡
原理:在每一个节点中加入一个随机域,依靠随机数实现性质,控制树的平衡,TREAP即是一颗二叉树也是一个堆。
特点:简单,容易实现
Treap节点数据:

structnode{
  intval,fix;              //val:节点数据,fix:随机数
  node *ch[2];         //左右孩子
}*root=NULL;                  //根节点

TREAP的主要操作:

1、旋转

2、查找、最大值、最小值

3、插入、删除

4、分离、合并

5、前驱、后继

6、第X个值

7、懒惰标志的应用

TREAP的旋转很简单,只有两种,左旋和右旋
代码:
void rotate(node * &cur,int f){
      node *p=cur->ch[!f];
      cur->ch[!f]=p->ch[f];
      p->ch[f]=cur;
      cur=p;
}

旋转节点为高处点,f代表旋转方向,1代表向右旋转,0代表向左
Treap节点插入及初始化:
  1. Treap查找最大值、最小值与普通二叉树查找方式相同
  2. Treap节点插入时先按照二叉排序树插入,然后按照随机数维护堆的性质
 
void insert(node * &cur,int x){
	if(!cur)cur=newnode(x);
	else{
		int bj=cur->val<x;
		insert(cur->ch[bj],x);
		if(cur->ch[bj]->fix<cur->fix)
			rotate(cur,!bj);
	}
}
node * newnode(int x){
	node * cur=new node;
	cur->val=x;
	cur->fix=rand();
	cur->ch[0]=cur->ch[1]=NULL;
	return cur;
}
                   



Treap节点删除:

情况一:左右子节点全部为NULL,则该节点直接设NULL即可

情况二:左右子节点有一个为NULL,则将该节点设为它的非NULL子节点
情况三:左右子节点都非NULL,通过旋转成以上情况再删除
           
void del(node * &cur,int x){
	if(!cur)return;
	if(cur->val > x)del(cur->ch[0],x);	//在左子树中查找删除
	else if(cur->val < x)del(cur->ch[1],x);	//在右子树中查找删除
	else{	//删除当前节点
		if(!cur->ch[0]||!cur->ch[1]){	//子树中有NULL
			node *t=cur;
			cur=(!cur->ch[0])?cur->ch[1]:cur->ch[0];
			delete t;
		}
		else {	//子树中无空
			int bj=cur->ch[0]->fix<cur->ch[1]->fix;
			rotate(cur,bj);
			del(cur->ch[bj],x);
		}	
	}	
}
TREAP分离和合并: 
分离:要把一个Treap按大小分成两个Treap,只要在需要分开的位置加一个虚拟节点,然后旋至根节点删除,左右两个子树就是得出的两个Treap了。根据二叉搜索树的性质,这时左子树的所有节点都小于右子树的节点。时间相当于一次插入操作的复杂度,也就是O(logn)
合并:TREAP合并的条件为第一颗TREAP的每一个节点的值小于第二颗的每一个节点的值,则增加一个虚拟节点,左右孩子分别为这两颗TREAP,在删除该虚拟节点。
Treap定值的前驱和后继:
求前驱的基本思想:贪心逼近法。在树中查找,一旦遇到一个不大于这个元素的值的节点,更新当前的最优的节点,然后在当前节点的右子树中继续查找,目的是希望能找到一个更接近于这个元素的节点。如果遇到大于这个元素的值的节点,不更新最优值,节点的左子树中继续查找。直到遇到空节点,查找结束,当前最优的节点的值就是要求的前驱。求后继的方法与上述相似。
node* pred(node* cur,node * f,int x)
node* secc(node* cur,node * f,int x)
Cur:当前节点,f记录前驱和后即,x标准值
node * pred(node * cur,node * f,int x){
	if(!cur)return f;
	if(x<cur->val)return pred(cur->ch[0],f,x);
	return pred(cur->ch[1],cur,x);
}
node * secc(node * cur,node * f,int x){
	if(!cur)return f;
	if(cur->val<x)return secc(cur->ch[1],f,x);
	return secc(cur->ch[0],cur,x);
}
//主程序中调用:
Node *p = Pred(root,null,e); 
Node *s = Succ(root,null,e); 
TREAPx值和懒惰标志: 
求第x值节点需要增加size;方法与其他二叉查找树相同。
node * find(node * cur,int x){
	int y=ssize(cur->ch[0]);
	if(y==x-1)return cur;
	else if(y>=x)return find(cur->ch[0],x);
	else return find(cur->ch[1],x-1-y);
}
懒惰标志的下传和数据更新:
int ssize(node * cur){
	return cur?cur->size:0;
}
void update(node * cur){
	if(!cur)return;
	cur->size=ssize(cur->ch[0])+ssize(cur->ch[1])+1;
}
void down(node * cur){
	if(cur->delta){
		if(cur->ch[0]){
			cur->ch[0]->delta+=cur->delta;
			cur->ch[0]->val+=cur->delta;
		}
		if(cur->ch[1]){
			cur->ch[1]->delta+=cur->delta;
			cur->ch[1]->val+=cur->delta;
		}
		cur->delta=0;
	}
}
TREAP值在树中的排序:
int px(Node *cur,int x,int js) 
{ 
	if (x ==cur->val)
		return cur->ch[0]->size + js + 1; //返回元素的排名
	else if (x < cur->val)
		return px(cur->ch[0],x,js); //在左子树中查找 
	else 
		return px(cur->ch[1],x,js + cur->ch[0]->size + cur->cnt); 
			//在右子树中查找
} 


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

七情六欲·

学生党不容易~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值