替罪羊树学习总结
替罪羊树学习总结ScapeGoat Tree
暴力即优雅——替罪羊树
学习链接
各种不同平衡树的算法其本质上的区别在于通过不同的方式维护树的形态,保证每次操作的时间复杂度为\(log\)。
替罪羊树的核心思想是暴力重构,设定一个参数\(a\in[0.5,1)\),其实取到0.5会T飞,对于每颗子树满足\(son1/son2.size<a*tree.size\)。
bool bad(int k)
{
int k1=t[k].son[0],k2=t[k].son[1];
return (double)max(t[k1].s,t[k2].s)>=(double)t[k].s*alpha;
}
变量解释
double alpha=0.75;//平衡因子
int s[100010];//重构数组
struct node{
int son[2],v,die,s;//两个儿子,权值,是否被删除,子树大小
}t[100010];
重构
void dfs(int k)
{
if(!k) return;
dfs(t[k].son[0]);if(!t[k].die)s[++top]=k;dfs(t[k].son[1]);
}
void build(int &k,int l,int r)
{
if(l>r)return;int mid=(l+r)/2;k=s[mid];
if(l==r)
{
t[k].s=1;t[k].son[0]=t[k].son[1]=t[k].die=0;return;
}
if(l<mid) build(t[k].son[0],l,mid-1);else t[k].son[0]=0;
if(r>mid) build(t[k].son[1],mid+1,r);else t[k].son[1]=0;
t[k].s=t[t[k].son[0]].s+t[t[k].son[1]].s+1;return;
}
void rebuild(int &k)
{
top=0;dfs(k);if(top)build(k,1,top);
}
插入操作
插入操作与普通BST基本无区别,但是每次插入完需要判断是否需要重构,找到深度最浅的需要重构的点进行重构。
void insert(int &k,int v)
{
if(!k)
{
k=++cnt;t[k].v=v;t[k].s=1;return;
}
if(v<=t[k].v) insert(t[k].son[0],v);
else insert(t[k].son[1],v);
int k1=t[k].son[0],k2=t[k].son[1];
t[k].s=t[k1].s+t[k2].s+exist(k);
if(!bad(k))
{
if(need)
{
if(need==t[k].son[0]) rebuild(t[k].son[0]);
else rebuild(t[k].son[1]);
}
need=0;
}
else need=k;
}
删除操作
删除操作并不需要真正删除节点,只需要给这个节点打上标记,在之后处理的时候忽略掉就可以啦
void delet(int k,int v)
{
if(!k) return;
int k1=t[k].son[0],k2=t[k].son[1];
if(!t[k].die&&t[k1].s+1==v){t[k].die=1;t[k].s--;return;}
if(t[k1].s>=v) delet(k1,v);
else delet(k2,v-t[k1].s-exist(k));
t[k].s=t[k1].s+t[k2].s+exist(k);
if(!bad(k))
{
if(need)
{
if(need==t[k].son[0]) rebuild(t[k].son[0]);
else rebuild(t[k].son[1]);
}
need=0;
}
else need=k;
}
查询操作
查询操作与普通BST操作没有区别。