在所有的平衡树中,为陈启峰提出的SBT最富盛名,并且最快最方便。
下面就是我对此的学习过程。
首先要了解二叉排序树,然后要明白了旋转在平衡过程中的重要地位。
有左旋和右旋之分,下面以右旋为例,左旋对称即可:
于是我们可以很清楚的得到右旋代码:
void RR(int &t){
int k=left[t];
left[t]=right[k];
right[k]=t;
s[k]=s[t];
s[t]=s[left[t]]+s[right[t]]+1;
t=k;
}
相对应的就有了左旋的:
void LR(int &t){
int k=right[t];
right[t]=left[k];
left[k]=t;
s[k]=s[t];
s[t]=s[left[t]]+s[right[t]]+1;
t=k;
}
给出性质a:s[right[t]]>=s[left[left[t]]],s[right[left[t]]]
性质b:s[left[t]]>=s[right[right[t]]],s[left[right[t]]]
说白了就是深度浅的结点数一定要大于深度深的,不然树就有所偏向了。
而maintain过程就是针对于插入过程中破环了这两个性质,我认为这个可以背出来,
举个例子加深理解:
插入到左子树的左儿子中,导致左子树结点数多了,就右旋,变到左边来;
插入到左子树的右儿子中,应先用左旋把最底下的儿子调上来,再整个右旋;
最后整个维护:
void maintain(int &t,bool flag){ //flag是指目前是插入了哪边的子树
if (!flag)
if (s[left[left[t]]]>s[right[t]])RR(t);
else
if (s[right[left[t]]]>s[right[t]]){
LR(left[t]);
RR(t);
}
else return ;
else
if (s[left[right[t]]] > s[left[t]])LR(t);
else
if (s[right[right[t]]]>s[left[t]]){
RR(right[t]);
LR(t);
}
else return ;
maintain(left[t],false);
maintain(right[t],true);//只需维护左假右真,对于左真右假在下面的两次调用中体现了,无需单独维护,证明略
maintain(t,true);
maintain(t,false);
}
于是,我们就可以很简单的写出对应的插入和删除过程:
void insert(int &t,int v){
if (t==0){
t=++cnt;
left[cnt]=0;
right[cnt]=0;
s[cnt]=1;
key[cnt]=v;
return;
}
s[t]++;
if (v<key[t])insert(left[t],v);
else insert(right[t],v);
maintain(t,v>=key[t]);
}
int Delete(int &t,int v){
s[t]--;
int Del;
if (v==key[t] || (v<key[t] && left[t]==0) || (v>key[t] && right[t]==0)){
Del=key[t];
if (left[t]==0 || right[t]==0)t=left[t]+right[t];
else key[t]=Delete(left[t],key[t]+1);
return Del;
}
if (v<key[t])Del=Delete(left[t],v);
else Del=Delete(right[t],v);
return Del;
}
至此,SBT就结束了。