伸展树(Splay)基本操作


1.旋转操作

inline void rotate(int x) //左旋和右旋操作 
{
	int y=fa[x],z=fa[y];//y是x的父节点,z是y的父节点 
	int b=(lc[y]==x)?rc[x]:lc[x];
	//if(x==lc[y]) b=rc[x];反之。选取不与y->x同侧的x的儿子b 
	fa[x]=z,fa[y]=x;
	if(b) fa[b]=y;//调整父子关系 
	if(z) (y==lc[z]?lc[z]:rc[z])=x;
	if(x==lc[y]) rc[x]=y,lc[y]=b;
	else lc[x]=y,rc[x]=b;//进一步调整左右子树关系 
}

2.Splay(x,target)     将伸展树中的节点x调整为target的左/右儿子,若要将x调整为根节点,将target设置为空节点即可。

inline void Splay(int x,int tar){
	while(fa[x]!=tar){
		if(fa[fa[x]]!=tar)
			Wrt(x)==Wrt(fa[x])?rotate(fa[x]):rotate(x);
		rotate(x); 
	}
	if(!tar) rt=x;
}

inline bool Wrt(int x)//判断x是否是父亲的右儿子 
{
	return rc[fa[x]]==x;
}

3.find(v)    判断v是否在伸展树中

inline void find(int v)
{
	int x=root;	//从根开始查找 
	while(x){
		if(v==val[x]) break;//找到了 
		if(v>val[x]) x=rc[x];//v比x权值大,在右子树中寻找 
		else x=lc[x];
	}
	if(x) Splay(x,0);//调整伸展树形态 
	return x;
} 

4.insert(v)    将v插入伸展树中

inline void insert(int v)//插入 
{
	int x=root,y,dir;//dir用于判断是右还是左
	while(x)
	{
		++sze[y=x];
		if(v


5.join(u,v)    将两棵伸展树s1 s2合并,u,v分别是这两棵树的根节点,且s1中所有元素都小于s2

具体方法:找到s1中权值最大的节点x,将其调整为根节点,将s2作为x的右子树

inline void join(int u,int v)
{
	fa[u]=fa[v]=0;
	int w=u;
	while(rc[w]) w=rc[w];
	Splay(w,0);//将w调整为根节点 
	rc[w]=v,fa[v]=w; 
}
 

6.delete(x)    删除x

执行Splay(x,0),然后join(lc[x],rc[x])即可。

inline void delete(int u)
{
	Splay(x,0);
	if(!lc[u]||!rc[u]) fa[root=lc[u]+rc[u]]=0;
	else join(lc[u],rc[u]);
	lc[u]=rc[u]=0;
} 

7.getrank(v)    查询v的排名(例如按权值从小到大排序)

首先查找到v,执行Splay(x,0), v的排名就是左子树节点个数+1

inline void getrank(int v)
{
	int x=find(v);
	return sze[lc[x]]+1;
}

8.Spilt(v)    以v为界,将伸展树分离成s1和s2,其中s1所有元素都小于s2

首先执行find(v),将其调整为根节点即可。代码就不赘述。

此上为八种基本操作。