【省选】算法总结——平衡树

130409总结——平衡树



其实到现在为止Treap还没写成功过(网上是指针,就照着指针写。。。。球了呗。。。)


不过后来还好,SBT是数组的,理解后想改成指针(看起来多好看的),是在无力啊。。。


想想还是算了,就数组吧,不容易出错






1.二叉查找树(二叉搜索树)BST


1.1基本思想


其实思想很简单,就是建立一颗二叉树,左子树都小于根,右子树都大于根,来实现快速查找(其实很慢。。。)



1.2插入


插入X,如果X<当前节点的值(key),就把X插入左子树,否则插入右子树(等于也在右子树,方便且不容易出错)



1.3查找


如果要查找一个数X,如果X<当前key,就在左子树找,否则就在当前节点或者右子树找



弊端


前面说这个似快非快是有道理的,比如一个递增序列,X1插入跟,X2进入右子树,X3进入右子树的右子树,。。。。,这样这棵树就退化成一条链了!

所以我们需要实现平衡!实现严格log2N


2.随机二叉查找树Treap


顾名思义,Treap= Tree + heap,所以也称作“树堆”

前面说了BST退化的原因在于树的根固定了,儿子有可能一直往一边跑,那我们可不可以通过变换把树平衡呢?先看下图



我们可以通过把23dia起来实现树的平衡,如下图



Treap的思想就是给每个节点多维护一个附加信息fix,给它一个随机值,然后在保证key为二叉查找树的基础上再给fix建立一个小(大)根堆,由于随机数是大致平均的,所以我们也可以实现大致的平衡,虽然不是严格log2N,但也可以保证快速了,还有就是代码比接下来要说的SBT


操作都差不多。。。。。



3.SizeBalanced Tree


3.1基本思想


Treap差不多,都是想办法让树平衡,不过Treap额外维护了一个没用的fix,并且还要靠RP,万一临考前RP不好就球了

SBT的核心思想就是Maintain函数,它不借助额外的信息,我们说树不平衡就是说某个子树的节点数(size)多了,所以我们就可以借助这个size来使这个树左右平衡!



3.2存储结构

可以开结构体,但是写出来代码很长(后面题目里面有一份是结构体的,很蛋疼。。。。)

所以还是开数组方便些

int S[N],L[N],R[N],A[N];//分别表示size,左儿子,右儿子,和当前key



3.3Maintain函数

为了描述方便用S[]表示sizeL[]表示左子树,R[]表示右子树

Maintain分两类

  1. 调整右子树,就比较S[R[R[p]]]S[L[p]],如果前者大,就把p左旋,树就平衡了;否则就再比较S[R[L[p]]]S[L[p]],如果前者大,就先把R[p]右旋,再把p左旋,整棵树就平衡了;否则return

  2. 调整左子树就与上面完全相反


通过上面的分析我们可以得知,核心思想就是把Size大的尽量向上走

void maintain(int &p,bool flag) //flag为true表示调整右子树
{  
	if(flag)  
		{  
		if(S[R[R[p]]]>S[L[p]]) rotate_left(p);  
		else if(S[R[L[p]]]>S[L[p]])  
		{  
			rotate_right(R[p]);  
			rotate_left(p);  
		}  
		else return;  
	}  
	else{  
		if(S[L[L[p]]]>S[R[p]]) rotate_right(p);  
		else if(S[L[R[p]]]>S[R[p]])  
		{  
			rotate_left(L[p]);  
			rotate_right(p);  
		}  
		else return;  
	}  
	maintain(L[p],0);  
	maintain(R[p],1);  
	maintain(p,1);  
	maintain(p,0);  
}



核心模块完了其他就好写了


3.4左旋右旋

void rotate_left(int &x)  
{  
	int y=R[x];  
	R[x]=L[y];  
	L[y]=x;  
	S[y]=S[x];  
	S[x]=S[L[x]]+S[R[x]]+1;  
	x=y;  
}  

void rotate_right(int &x)  
{  
	int y=L[x];  
	L[x]=R[y];  
	R[y]=x;  
	S[y]=S[x];  
	S[x]=S[L[x]]+S[R[x]]+1;  
	x=y;  
}  





3.5插入

void insert(int &p,int k)  
{  
	if(!p) {p=++total;init(p,k);}  
	else{  
		S[p]++;  
		if(k<A[p]) insert(L[p],k);  
		else insert(R[p],k);  
		maintain(p,k>=A[p]);  
	}  
}  




3.6K大(整个区间)

int kth(int &p,int k)  
{  
	int tmp=S[L[p]]+1;  
	if(k==tmp) return A[p];  
	else if(k<tmp) return kth(L[p],k);  
	else return kth(R[p],k-tmp);  
}  



3.7找最值

显然,最值肯定出现在整棵树最左(右)

int getmin()  
{  
	int x;  
	for(x=root ;L[x];x= L[x]);  
	return A[x];  
} 
int getmax()  
{  
	int x;  
	for(x=root ;R[x];x=R[x]);  
	return A[x];  
}  





3.8删除

删除操作要是题目要求而写的很不同,所以就不写了





差不多就这些了。。。





目前只做过一道SBT题目(存储方式结构体和数组都有)

SBT[NOI2004]郁闷的出纳员cashierhttp://blog.csdn.net/jiangzh7/article/details/8759397













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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值