替罪羊应该是所有平衡树中最简单的了(但这东西是真的恶心),它的主要思想是在发现子树不平衡时把子树拍平重建。
首先我们考虑什么时候我们认为这个子树是不平衡的。
我们可以设置一个常量 \(eps\),当有一棵子树的大小超过了它父节点子树大小乘 \(eps\),那么我们就可以重建这棵子树了。
一般情况下我们选择 0.75 作为 \(eps\) 的值。
重建过程就差不多是模拟了。我们中序遍历这棵子树,把所有节点都记录下来。然后再递归去建树。
对于一个区间 \(\left[L, R\right]\) 我们把最中间的那个树当作新的根节点,这样能使其更平衡,再递归建立左右子树。
重建代码如下:
bool Check(int u) { // 判断是否要重建
return 1.0 * max(w[w[u].l].s, w[w[u].r].s) > eps * w[u].s;
}
void Mid(int u) { // 中序遍历(左->根->右)
if (u) {
Mid(w[u].l);
w[u].c && (p[++cnt] = u); // 把节点记录下来
Mid(w[u].r);
}
}
int Build(int l, int r) { // 建树
if (l <= r) {
int mid = l + r >> 1;
w[p[mid]].l = Build(l, mid - 1); // 递归建左子树
w[p[mid]].r = Build(mid + 1, r); // 递归建右子树
Push_up(p[mid]); // 建立当前节点
return p[mid]; // 返回当前节点编号
}
return 0;
}
int rBuild(int u) { // 拍平+重建+返回重建后子树的根(记得把rBuild(u)的值赋值给u)
cnt = 0; // 清空回收的个数
Mid(u); // 把节点记录下来
return Build(1, cnt); // 建树
}
标签:子树,return,int,替罪羊,mid,笔记,平衡,节点,重建
From: https://www.cnblogs.com/caoshurui/p/18042040