伸展树—作用介绍

伸展树,也叫分裂树,是一种二叉排序树,它能在O(log n)内完成插入、查找和删除操作。

在伸展树上的一般操作都基于伸展操作:假设想要对一个二叉查找树执行一系列的查找操作,为了使整个查找时间更小,被查频率高的那些条目就应当经常处于靠近树根的位置。于是想到设计一个简单方法, 在每次查找之后对树进行重构,把被查找的条目搬移到离树根近一些的地方。伸展树应运而生。伸展树是一种自调整形式的二叉查找树,它会沿着从某个节点到树根之间的路径,通过一系列的旋转把这个节点搬移到树根去。


伸展树所需的值如下:

int root;
struct node
{
	int d,n,f,c,son[2];
}tr[ ];

root:整棵树的总根

d:自己的值

n:与d同值的个数

f(ather):tr[ ]的父亲

c(ontrol):tr[ ]所管理的孩子总数

son[0],son[1]:左孩子,右孩子


让x作rt的孩子的翻转方法:

有5种情况:

1、如下图左边的情况(红色),若将1摇到该树根的位置,先把2右旋至根的位置,再把1右旋至根的位置;

2、如下图右边的情况(蓝色),若将3摇到该树根的位置,先把2左旋至根的位置,再把1左旋至根的位置;

3、如下图左边的情况(红色),若将2摇到该树根的位置,先把2左旋至1的位置,再把2右旋至根的位置;

4、如下图右边的情况(蓝色),若将2摇到该树根的位置,先把2右旋至3的位置,再把2右旋至根的位置。

5、x的爷爷是rt,那我们只要做一次翻转。

旋转过程中,认父亲的顺序是:我的孩子->我的父亲,我->我的爷爷,我的父亲->我。这样,旋转后的关系就处理好了。

插入值d的办法:

1、找(findip)到接近d的一个叶子节点,2、把值d加(add)在叶子处 或 如已有相同值的点,则直接在该点的n上+1。

例题:(来源:codevs 4543)

写一种数据结构,来维护一些数,其中需要提供以下操作:

1. 插入x数

2. 删除x数(若有多个相同的数,因只删除一个)

3. 查询x数的排名(若有多个相同的数,因输出最小的排名)

4. 查询排名为x的数

5. 求x的前驱(前驱定义为小于x,且最大的数)

6. 求x的后继(后继定义为大于x,且最小的数) 

代码:

#include<cstdio>
#include<cstring>
using namespace std;

int root=0;
struct node
{
	int d,n,f,c,son[2];
}tr[100010];int len=0;

void add(int d,int f)//增加以f为父亲的值为d的点
{
	len++;
	tr[len].d=d;tr[len].f=f;tr[len].c=tr[len].n=1;
	tr[len].son[0]=tr[len].son[1]=0;
	if(d<tr[f].d) tr[f].son[0]=len;
	else tr[f].son[1]=len;
}

int findip(int d)//查找值接近d的点
{
	int x=root;
	while(tr[x].d!=d)
	{
		if(d<tr[x].d)
		{
			if(tr[x].son[0]==0) break;
			x=tr[x].son[0];
		}
		else
		{
			if(tr[x].son[1]==0) break;
			x=tr[x].son[1];
		}
	}
	return x;
}

void update(int x)
{
	tr[x].c=tr[x].n+tr[tr[x].son[0]].c+tr[tr[x].son[1]].c;
}

void rotate(int x,int w)//翻转 w=0左旋 w=1右旋
{
	int f=tr[x].f;
	int ff=tr[f].f;
	int r,R;
	
	//我的孩子->我的父亲
	R=f;r=tr[x].son[w];
	tr[R].son[1-w]=r;
	if(r!=0) tr[r].f=R;
	
	//我->我的爷爷
	R=ff;r=x;
	if(tr[ff].son[0]==f) tr[R].son[0]=r;
	else tr[R].son[1]=r;
	tr[r].f=R;
	
	//我的父亲->我
	R=x;r=f;
	tr[R].son[w]=r;
	tr[r].f=R;
	
	update(f);
	update(x);
}

void splay(int x,int rt)//通过翻转,让x作rt的孩子
{
	while(tr[x].f!=rt)
	{
		int f=tr[x].f;
		int ff=tr[f].f;
		if(rt==ff)
		{
			if(tr[f].son[0]==x) rotate(x,1);
			else rotate(x,0);
		}
		else
		{
				 if(tr[ff].son[0]==f&&tr[f].son[0]==x){rotate(f,1);rotate(x,1);}
			else if(tr[ff].son[1]==f&&tr[f].son[1]==x){rotate(f,0);rotate(x,0);}
			else if(tr[ff].son[0]==f&&tr[f].son[1]==x){rotate(x,0);rotate(x,1);}
			else if(tr[ff].son[1]==f&&tr[f].son[0]==x){rotate(x,1);rotate(x,0);}
		}
	}
	if(rt==0) root=x;
}

void ins(int d)//插入1个值d的点
{
	if(root==0){add(d,0);root=len;return ;}
	int x=findip(d);
	if(tr[x].d==d)
	{
		tr[x].n++;
		update(x);
		splay(x,0);
	}
	else
	{
		add(d,x);
		update(x);
		splay(len,0);
	}
}

int findqianqu(int d)//x<d
{
	int x=findip(d);splay(x,0);
	/*if(tr[x].d<d){}
	else*/if(tr[x].d>=d&&tr[x].son[0]!=0)
	{
		x=tr[x].son[0];
		while(tr[x].son[1]!=0) x=tr[x].son[1];
	}
	else if(tr[x].d>=d/*&&tr[x].son[0]==0*/) x=0;
	return x;
}

int findhouji(int d)//x>d
{
	int x=findip(d);splay(x,0);
	/*if(tr[x].d>d){}
	else*/if(tr[x].d<=d&&tr[x].son[1]!=0)
	{
		x=tr[x].son[1];
		while(tr[x].son[0]!=0) x=tr[x].son[0];
	}
	else if(tr[x].d<=d/*&&tr[x].son[1]==0*/) x=0;
	return x;
}

void del_1(int d)//删除1个值为d的点
{
	int x=findip(d);splay(x,0);
	if(tr[x].n>1){tr[x].n--;update(x);}
	else if(tr[x].son[0]==0&&tr[x].son[1]==0){root=0;len=0;}
	else if(tr[x].son[0]==0&&tr[x].son[1]!=0){root=tr[x].son[1];tr[root].f=0;}
	else if(tr[x].son[0]!=0&&tr[x].son[1]==0){root=tr[x].son[0];tr[root].f=0;}
	else
	{
		int p=tr[x].son[0];
		while(tr[p].son[1]!=0) p=tr[p].son[1];
		splay(p,x);
		
		int R=p,r=tr[x].son[1];
		tr[R].son[1]=r;
		tr[r].f=R;
		
		root=R;tr[root].f=0;//
		update(R);
	}
}

void del_2(int l,int r)//删除区间l-r
{
	int lc=findqianqu(l),rc=findhouji(r);
	splay(lc,0);splay(rc,lc);
	tr[rc].son[0]=0;
	update(rc);update(lc);
}

int findpaiming(int d)//查找值d的排名(即查找值小于d的点的个数)
{
	int x=findip(d);splay(x,0);
	return tr[tr[x].son[0]].c+1;
}

int findshuzi(int k)//查找排第k的点的值
{
	int x=root;
	while(1)
	{
		int lc=tr[x].son[0],rc=tr[x].son[1];
		if(k<=tr[lc].c)x=lc;
		else if(k>tr[lc].c+tr[x].n){k-=tr[lc].c+tr[x].n;x=rc;}
		else break;
	}
	splay(x,0);
	return tr[x].d;
}

int main()
{
	int n;scanf("%d",&n);
	while(n--)
	{
		int opt,x;scanf("%d%d",&opt,&x);
		if(opt==1) ins(x);
		else if(opt==2) del_1(x);
		else if(opt==3) printf("%d\n",findpaiming(x));
		else if(opt==4) printf("%d\n",findshuzi(x));
		else if(opt==5) printf("%d\n",tr[findqianqu(x)].d);
		else if(opt==6) printf("%d\n",tr[findhouji(x)].d);
	}
	return 0;
}



推荐:《伸展树—系列题目》http://blog.csdn.net/a_bright_ch/article/details/72801820

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值