title : 平衡树专题之替罪羊树
date : 2022-2-8
tags : ACM,数据结构,平衡树
author : Linno
替罪羊树
替罪羊树是一种非常优(bao)雅(li)的平衡树,与普通平衡树的区别是,它维护平衡的方式不是旋转,而是重构。尽管重构的过程是非常暴力的,复杂度依然是很好的 O ( l o g n ) O(logn) O(logn)
准备工作
需要储存的信息:左右子树编号、当前结点的值、以当前结点为根的树的大小和实际大小、删除标记。
struct Node
int l,r,val; //左右子树编号和结点的值
int sz,fact; //树的大小和<实际大小>
bool exist; //删除标记
}tzy[maxn]
新建节点
在所在结点now建立一个值为val的结点。
void newnode(int &now,int val){
now=++cnt;
tzy[now].val=val;
tzy[now].sz=tzy[now].fact=1;
tzy[now].ex=1;
}
插入结点
与平衡树基本操作一致。
void ins(int &now,int val){
if(!now){
newnode(now,val);//从now开始插入val
check(root,now); //从root到now检查是否重构
return;
}
tzy[now].sz++;
tzy[now].fact++;
if(val<tzy[now].val) ins(tzy[now].l,val); //往左边插入
else ins(tzy[now].r,val); //往右边插入
}
删除操作
在将要被删除的结点上打一个标记,称之为“惰性删除”。
void del(int &now,int val){
if(tzy[now].exist&&tzy[now].val==val){
tzy[now].exist=false; //打删除标记
tzy[now].fact--;
check(root,now)//检查是否平衡
return;
}
tzy[now].fact--;
if(val<tzy[now].val) del(tzy[now].l,val);
else del(tzy[now].r,val);
}
检查并判断是否重构
进行插入和删除之后需要检查树是否需要重构。
需要重构的条件:当前结点的左子树或右子树的大小大于当前结点的大小乘一个平衡因子alpha(一般在0.5~1之间)
平衡因子alpha必须取0.5~1之间的数比如0.75
这里解释为什么要区分size和fact:被删除结点的过多也会影响后续操作的效率。
bool imbalance(int now){
//判断当前结点是否平衡
if(max(tzy[tzy[now].l].size,tzy[tzy[now].r].size)>tzy[now].size*alpha||tzy[now].size-tzy[now].fact>tzy[now].size*0.3) return true;
return false;
}
void update(int now,int end){
if(!now) return;
if(tzy[end].val<tzy[now].val) update(tzy[now].l,end);
else update(tzy[now].r,end);
tzy[now].size=tzy[tzy[now].l].size+tzy[tzy[now].r].size+1;
}
void check(int &now,int end){
//检查当前结点到end的树是否平衡
if(now==end) return;
if(imbalence(now)