平衡树学习笔记(替罪羊)

替罪羊应该是所有平衡树中最简单的了(但这东西是真的恶心),它的主要思想是在发现子树不平衡时把子树拍平重建。

首先我们考虑什么时候我们认为这个子树是不平衡的。
我们可以设置一个常量 \(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);  //  建树
}
posted @ 2024-02-28 21:58  caoshurui  阅读(21)  评论(0)    收藏  举报