简单平衡树 (ノ`д´)ノ ミ ∵.: ┻━┻:·

简单平衡树


Splay算法

旋转操作

旋转过程如下图所示:
右旋 & 左旋

代码如下:

bool get(int x) {
	return son[f[x]][1]==x;
}
void update(int x) {
	if(x){
		siz[x]=cou[x];
		if(son[x][0])siz[x]+=siz[son[x][0]];
		if(son[x][1])siz[x]+=siz[son[x][1]];
	}
}

void rotate(int x) {
	int old=f[x],oldf=f[old],whichson=get(x);
	son[old][whichson]=son[x][whichson^1],f[son[old][whichson]]=old;
	son[x][whichson^1]=old,f[old]=x;
	f[x]=oldf;
	if(oldf) {
		son[oldf][son[oldf][1]==old]=x;
	}
	update(old);
	update(x);
}

splay操作

x x x旋到根节点以保证均摊复杂度
代码如下:

void splay(int x) {
	for(int fa;fa=f[x];rotate(x))
		if(f[fa])
			rotate((get(x)==get(fa))?fa:x);
	root=x;
}

插入操作

加入节点。
1.如果 r o o t root root= 0 0 0,说明树为空,特判加入即可。
2.如果 不为空,则
(1). 如果存在于x权值相同的节点,那么 c o u [ cou[ cou[节点标号 ] ] ]++,再用 s p l a y splay splay维护一下 s i z siz siz
(2). 如果不存在,那么新开一个节点,再用 s p l a y splay splay维护一下 s i z siz siz

void insert(int x) {
	if(root==0) {
		sz++;
		son[sz][0]=son[sz][1]=f[sz]=0;
		key[sz]=x;
		cou[sz]=siz[sz]=1;
		root=sz;
		return;
	}
	int now=root,fa=0;
	while(1) {
		if(x==key[now]) {
			cou[now]++;
			update(now);
			update(fa);
			splay(now);
			break;
		}
		fa=now;
		now=son[now][key[now]<x];
		if(now==0) {
			sz++;
			f[sz]=fa;
			son[sz][0]=son[sz][1]=0;
			cou[sz]=siz[sz]=1;
			son[fa][key[fa]<x]=sz;
			key[sz]=x;
			update(fa);
			splay(sz);
			break;
		}
	}
}

查询一个数的排名

  1. 如果 x x x小于 k e y [ n o w ] key[now] key[now],就寻找 n o w now now的左儿子
  2. 如果 x x x等于 k e y [ n o w ] key[now] key[now],就直接 r e t u r n ( a n s return (ans return(ans+ 1 ) 1) 1)
  3. 如果 x x x大于 k e y [ n o w ] key[now] key[now],那么 a n s ans ans+= s i z [ s o n [ n o w ] [ 0 ] ] siz[son[now][0]] siz[son[now][0]]+ c o u [ n o w ] cou[now] cou[now],然后寻找 n o w now now的右儿子
int find(int x){
	int now=root,ans=0;
	while(1){
		if(x<key[now]) now=son[now][0];
		else{
			ans+=(son[now][0]?siz[son[now][0]]:0);
			if(x==key[now]) {
				splay(now);
				return ans+1;
			}
			ans+=cou[now];
			now=son[now][1];
		}
	}
}

查询排名为k的数

  1. 如果 n o w now now左儿子的大小> x x x,则直接寻找 n o w now now的左儿子
  2. 如果 n o w now now左儿子的大小和 n o w now now的大小之和大于等于 x x x,则 k e y [ n o w ] key[now] key[now]就是答案
  3. 如果 n o w now now左儿子的大小和 n o w now now的大小之和小于 x x x,则 x − = n o w x-=now x=now左儿子的大小和 n o w now now的大小之和,然后在 n o w now now右子树里寻找第x大的元素
int kth(int x) {
	int now=root;
	while(1) {
		if(son[now][0] && x<=siz[son[now][0]]) {
			now=son[now][0];
		} else {
			int temp=(son[now][0]?siz[son[now][0]]:0)+cou[now];
			if(x<=temp)return key[now];
			x-=temp;
			now=son[now][1];
		}
	}
}

前驱、后继

int pre(){
	int now=son[root][0];
	while(son[now][1])now=son[now][1];
	return now;
}
int next(){
	int now=son[root][1];
	while(son[now][0])now=son[now][0];
	return now;
}

删除节点

void del(int x){
	int whatever=find(x);
	if(cou[root]>1) {
		cou[root]--;
		update(root);
		return;
	}
	if(!son[root][0]&&!son[root][1]) {
		clear(root);
		root=0;
		return;
	}
	if(!son[root][0]) {
		int oldroot=root;
		root=son[root][1];
		f[root]=0;
		clear(oldroot);
		return;
	}
	if(!son[root][1]) {
		int oldroot=root;
		root=son[root][0];
		f[root]=0;
		clear(oldroot);
		return;
	}
	int leftbig=pre(),oldroot=root;
	splay(leftbig);
	son[root][1]=son[oldroot][1];
	f[son[root][1]]=root;
	clear(oldroot);
	update(root);
	return;
}

Treap算法

T r e a p Treap Treap= t r e e tree tree+ h e a p heap heap
S p l a y Splay Splay的基础上多记录一个随机值 r d [ i ] rd[i] rd[i],表示改点的随机值,要求随机值排成一个大根堆,来保证树的深度。(可选择不记录 f a t h e r father father

旋转操作

s p l a y splay splay类似,不同的是 r o t a t e rotate rotate函数有不同:

void rotate(int &x,int y){
	int i=son[x][y^1];
	son[x][y^1]=son[i][y];
	son[i][y]=x;
	push_up(x);
	push_up(i);
	x=i;
}

插入操作

void ins(int p,int x){
	if(!p){
		p=++sz;
		siz[p]=cnt[p]=1;
		key[p]=x;
		rd[p]=rand();
		return;
	}
	if(key[p]==x){
		cnt[p]++;
		siz[p]++;
		return;
	}
	int d=(x>key[p]);
	ins(son[p][d],x);
	if(rd[p]<td[son[p][d]))
		rotate(p,d^1);
	push_up(p);
}

删除操作

void del(int &p,int x){
	if(!p) return;
	if(x!=key[p]) del(son[o][x>key[p]],x);
	else{
		if(!son[p][0]&&!son[p][1]){
			cnt[p]--;
			siz[p]--;
			if(!cnt[p]) p=0;
		}
		else if(son[p][0]&&!son[p][1]){
			rotate(p,1);
			del(son[p][1],x);
		}
		else if(!son[p][0]&&son[p][1]){
			rotate(p,0);
			del(son[p][0],x);
		}
		else{
			iny d=rd[son[p][0]>rd[son[p][1]];
			rotate(p,d);
			del(son[p][d],x);
		}
	}
	push_up(p);
}

前驱、后继

int pre(int p,int x){
	if(!p) return -INF;
	if(key[p]>=x) return pre(son[p][0],x);
	else return max(key[p],pre(son[p][1],x));
}
int suf(int p,int x){
	if(!p) return INF;
	if(key[p]<=x) return suf(son[p][1],x);
	else return min(key[p],pre(son[p][0],x));
}

查询操作与splay基本相同,不再介绍。


无旋Treap

因为 t r e a p treap treap的常数远小于 s p l a y splay splay,而难以进行区间操作。
所以,我们引入了无旋 t r e a p treap treap,又称 F H Q t r e a p FHQ treap FHQtreap
支持可持久化。

分裂

void split(int rt,int &a,int &b,int v){
	if(!rt){a=b=0;return;}
	if(t[rt].v<=v){
		a=rt;
		split(t[rt].rc,t[a].rc,b,v);
	}
	else{
		b=rt;
		split(t[rt].lc,a,t[b].lc,v);
	}
	update(rt);
}

合并

void merge(int &rt,int a,int b){
	if(!a||!b){rt=a+b;return;}
	if(t[a].rnk<t[b].rnk){
		rt=a;
		merge(t[rt].rc,t[a].rc,b);
	}
	else{
		rt=b;
		merge(t[rt].lc,a,t[b].lc);
	}
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值