平衡树,有代码推理过程

平衡树
一、二叉查找树
二叉查找树(Binary Search Tree)它或者是一棵空树;或者是具有下列性质的二叉树:
(1) 树中每个结点被赋予了一个权值;(下面假设不同结点的权值互不相同。)
(2) 若左子树不空,则左子树上所有结点的值均小于它的根结点的值;
(3) 若右子树不空,则右子树上所有结点的值均大于它的根结点的值;
(4) 左、右子树也分别为二叉查找树;

二叉查找树能够支持多种动态集合操作,只要所维护的数据集合存在偏序关系(简单来
说就是定义了小于等于)。因此,在信息学竞赛中,二叉查找树起着非常重要的作用,它可
以被用来表示有序集合、建立索引或优先队列等。我们也常常利用它动态地维护一个有序数
集,利用二叉查找树找到新加入序列中的数插入的位置。

二、Splay模板

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int root,len;

//d为值,f为父亲的编号,c为控制的节点个数,n为同值的节点个数
struct trnode{
	int d,f,c,n,son[2];//son存儿子节点的编号 
}tr[110000];

//更新x所控制的节点数 
void update(int x){
	int lc=tr[x].son[0],rc=tr[x].son[1];
	tr[x].c=tr[lc].c+tr[rc].c+tr[x].n;
}

//添加值为d的点,认编号为f的节点为父亲,同时,f也认它为孩子
void add(int d,int f){
	len++;
	tr[len].d=d,tr[len].n=1,tr[len].c=1,tr[len].f=f;
	if(d<tr[f].d) tr[f].son[0]=len;
	else tr[f].son[1]=len;
	tr[len].son[0]=tr[len].son[1]=0;
}

//找值为d的节点的地址,如果不存在d,有可能是接近d的(或大或小)
int findip(int d){
	int x=root;
	while(tr[x].d!=d){
		if(d<tr[x].d){
			if(tr[x].son[0]==0) break;
			else x=tr[x].son[0];
		}
		else{
			if(tr[x].son[1]==0) break;
			else x=tr[x].son[1];
		}
	}
	return x;
}


//w=1为右旋,w=0为左旋 
void rotate(int x,int w){
	int f=tr[x].f,ff=tr[f].f;
	int r,R;
	r=tr[x].son[w],R=f,tr[R].son[w^1]=r;
	if(r!=0) tr[r].f=R;
	
	r=x; R=ff;
	if(tr[ff].son[0]==f) tr[R].son[0]=r;
	else tr[R].son[1]=r;
	tr[r].f=R;
	
	r=f;R=x;
	tr[R].son[w]=r;
	tr[r].f=R;
	
	update(f),update(x);
}
void splay(int x,int rt){
	while(tr[x].f!=rt){
		int f=tr[x].f,ff=tr[f].f;
		if(ff==rt){
			if(x==tr[f].son[0]) rotate(x,1);
			else rotate(x,0);
		}
		else{
			    if(tr[f].son[0]==x&&tr[ff].son[0]==f) rotate(f,1),rotate(x,1);
			else if(tr[f].son[1]==x&&tr[ff].son[0]==f) rotate(x,0),rotate(x,1);
			else if(tr[f].son[0]==x&&tr[ff].son[1]==f) rotate(x,1),rotate(x,0);
			else if(tr[f].son[1]==x&&tr[ff].son[1]==f) rotate(f,0),rotate(x,0);
		}
		if(rt==0) root=x;
	}
}
void ins(int d){
	if(root==0){
		add(d,len),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);
	}
}

void del(int d){
	int x=findip(d);splay(x,0);
	if(tr[x].d!=d) return;
	if(tr[x].n>1){tr[x].n--;update(x);return;}
	if(tr[x].son[0]==0&&tr[x].son[1]==0){len=0;root=0;}
	else if(tr[x].son[0]!=0&&tr[x].son[1]==0){
		root=tr[x].son[0];
		tr[root].f=0;
	}
	else if(tr[x].son[1]!=0&&tr[x].son[0]==0){
		root=tr[x].son[1];
		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=tr[x].son[1],R=p;
		tr[R].son[1]=r;
		tr[r].f=R;
		
		root=R,tr[root].f=0;
		update(R);
	}
}

int findpaiming(int d){
	int x=findip(d);
	splay(x,0);
	return tr[tr[x].son[0]].c+1;
}
int findshuzi(int 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 findqianqu(int d){
	int x=findip(d);splay(x,0);
	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];
	}
	if(tr[x].d>=d) x=0;
	return x;
}
int findhouji(int d){
	int x=findip(d);splay(x,0);
	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];
	}
	if(tr[x].d<=d) x=0;
	return x;
}
int main(){
	int n;scanf("%d",&n);
	root=0;len=0;
	for(int i=1;i<=n;++i){
		int cz,x;scanf("%d%d",&cz,&x);
		if(cz==1) ins(x);
		if(cz==2) del(x);
		if(cz==3) printf("%d\n",findpaiming(x));
		if(cz==4) printf("%d\n",findshuzi(x));
		if(cz==5) printf("%d\n",tr[findqianqu(x)].d);
		if(cz==6) printf("%d\n",tr[findhouji(x)].d);
	}
	return 0;
	
}

操作有:插入,删除,询问排名,询问第k大,查找前驱后继。

一、ROTATE

对于rotate(x,w),表示将x向上旋转一层,w=1为左旋(Zag),w=0为右旋(Zig);

以w=1为例(如下图所示),即x是f的左儿子,此时使用右旋(Zig)操作。

(反之,若x是f的右儿子,则使用左旋(Zag)操作,过程类似。)

int f=tr[x].f;    //x的父亲

int ff=tr[f].f;    //x的祖父

int r,R;

其中A、B为x的左右子树,C为f右子树


  

以下过程中可以把w=1代进去。(son[1]为右儿子,son[0]为左儿子)

第一步:处理x与f之间的关系

r=tr[x].son[w],R=f;
tr[R].son[w^1]=r;
if(r!=0) tr[r].f=R;

B原是x的右子树,变到f的左子树

第二步:处理x与ff之间的关系

r=x; R=ff;
if(tr[ff].son[0]==f) tr[R].son[0]=r;
else tr[R].son[1]=r;
tr[r].f=R;

把x设为ff的儿子,若f是ff的左儿子,x就是ff的左儿子,反之为右儿子

第三步:整理f和x的关系

r=f;R=x;
tr[R].son[w]=r;
tr[r].f=R;

 x和f之间互相连上,就旋转完成了

二、Splay.

对于Splay(int x,int rt),表示将x旋转到rt的下面。

while(tr[x].f!=rt){
    int f=tr[x].f,ff=tr[f].f;
    if(ff==rt){
        if(x==tr[f].son[0]) rotate(x,1);
        else rotate(x,0);
    }
    else{
            if(tr[f].son[0]==x&&tr[ff].son[0]==f) rotate(f,1),rotate(x,1);
        else if(tr[f].son[1]==x&&tr[ff].son[0]==f) rotate(x,0),rotate(x,1);
        else if(tr[f].son[0]==x&&tr[ff].son[1]==f) rotate(x,1),rotate(x,0);
        else if(tr[f].son[1]==x&&tr[ff].son[1]==f) rotate(f,0),rotate(x,0);
    }
    if(rt==0) root=x;
}

 

①while语句:如果x的father不是rt,就把x往上旋。

while(tr[x].f!=rt)

②若x的祖父是rt,那么x的father就在rt的下面,只需要把x向上旋转一下,x就在rt下面了。

if(x==tr[f].son[0]) rotate(x,1);
    else rotate(x,0);

如果x是它父亲的左儿子,则右旋;否则左旋。

③剩下的情况就是:x的祖父也不是rt,那么就把x往上旋转两次,根据父亲与儿子的关系而定。

----------------------------------------------------------------------------------------------

下面举旋转的一种情况。f是ff左儿子,x是f左儿子(如下图)。

    if(tr[f].son[0]==x&&tr[ff].son[0]==f)

    rotate(f,1),rotate(x,1);

原状态:

先把f右旋,变成这样:

然后把x右旋,就旋转完成了,变成这样:

———————————————————————————————

最后,如果rt==0,就是把x旋转到0的下面,相当于把x旋转到根节点上面去,那么就要把root的值更新为x。

猜你喜欢

转载自blog.csdn.net/g21wcr/article/details/82932032