平衡树

fhq treap(无旋treap)
treap:其中每个点有输入值与随机值两种值,输入值满足二叉树性质,随机值满足堆性质。
而fhq如何实现平衡呢
它需要两个操作:分裂与合并

分裂:

目的:将其分成所有权值都大于或小于等于某一值的两棵平衡树
我们现在需要两个指针,每一次走到该点权值是否大于该值的反方向

点击查看代码
//pushup()一般是用来更改每一点的size...
void split(int &a,int &b,int p,int val){
	if(p==0){
		a=b=0;
		return ;
	}
	if(e[p].sm<=val){//此处val为进行分离的那一个值
		a=p;
		split(e[p].r,b,e[p].r,val);
	}
	else{
		b=p;
		split(a,e[p].l,e[p].l,val);
	}
	pushup(p);
}

合并:

保证,两颗平衡树中的某一颗所有值都比另一颗的大
仍使用两个指针指向两个平衡树,
对于当前指向有两种连接方式:把小的作为大的左儿子;把大的作为小的的右儿子
这是我们把随机值小的放上面
这个位置直接连接到上一节点并把指针放到该位置上。

点击查看代码
int merge(int x,int y){
	if(!x||!y) return x+y;
	if(e[y].dat<=e[x].dat){
		e[y].l=merge(x,e[y].l);
		pushup(y);
		return y;
	}
	else{
		e[x].r=merge(e[x].r,y);
		pushup(x);
		return x;
	}
}
接下来就是一些基础操作:

add:
按插入值分裂,再分别把该值与两棵树合并

点击查看代码
void add(int sm){
	int x,y;
	split(x,y,rt,sm);
	rt=merge(x,merge(newnode(sm),y));
}

delete:
按删除值大于分裂,再按其-1大于分裂,中间树删根或大小-1,再全部合并起来。

点击查看代码
void delete(int sm){
	int x,y,z;
	split(x,z,rt,sm);
	split(x,y,x,sm-1);
	y=merge(e[y].l,e[y].r);
	rt=merge(x,merge(y,z));
	
}

值查询排名(rank):
按该值分裂,观察左子树size。

点击查看代码
void rank(int sm){
	int x,y,ans;
	split(x,y,rt,sm);
	ans=e[e[x].l].siz;
	rt=merge(x,y);
	return ans;
}

排名查询值(rerank):
所有二叉平衡树的写法都一样。(略)

点击查看代码
int rerank(int p,int sm){
	if(e[e[p].l].siz+1<sm) return rerank(e[p].r,sm-e[e[p].l].siz-1);
	if(e[e[p].l].siz<=sm) return rerank(e[p].l,sm);
	return e[p].sm; 
}

前驱(fpre):

按值分裂,直接在A子树中rerank最后一名。

点击查看代码
int fpre(int sm){
	int x,y,ans;
	split(x,y,rt,sm-1);
	ans=rerank(x,e[x].siz);
	rt=merge(x,y);
	return ans;
}

后继(fnxt):
类似前驱。

点击查看代码
按值分裂,直接在A子树中rerank最后一名。
int fnxt(int sm){
	int x,y,ans;
	split(x,y,rt,sm);
	ans=rerank(y,1);
	rt=merge(x,y);
	return ans;
}
posted @ 2022-02-26 08:28  蒟蒻jht  阅读(89)  评论(0)    收藏  举报