替罪羊树
替罪羊树 是啥? 就是一个平衡树,只不过没有旋转操作
那遇到不平衡的咋办呢? 重构。
怎么重构?先求出不平衡子树的中序遍历(这个中序遍历肯定是有序的递增的)于是就可以分治建树,取中间那个点当根左边的当左子树、右边的当右子树……递归下去
于是就把不平衡的子树变成平衡的子树。
例题:洛谷模板 P3369
几种操作
结构体存的信息:
struct Node
{
int l,r;//左右节点编号
int val;//当前节点的值
int flag;//当前节点是否被删除 删除为0 没删除为1
int size;//size 是这颗子树的大小(包含被删除的节点)
int fact;//fact是实际的大小(不包含删除的节点)
}node[maxn];
添加节点:二叉搜索树那样添加就好,最后添加完了之后判断是否需要重构。
void ins(int x,int& no)
{
if(!no)
{
no = newnode(x);
check(root,no);
return;
}
node[no].fact ++ ;
node[no].size ++ ;
if(x < node[no].val)
ins(x,node[no].l);
else
ins(x,node[no].r);
}
删除节点:打上标记就好、在重构的时候完全删去。
void del(int x,int no)
{
if(node[no].flag && node[no].val == x)
{
node[no].flag = 0;
node[no].fact -- ;
check(root,no);
return;
}
node[no].fact--;
if(x < node[no].val)
del(x,node[no].l);
else
del(x,node[no].r);
}
重构还有一个作用: 完全删去删除点的节点。怎么做到这一点?
重构是把中序遍历的序列分治建树得到的。所以就可以在中序遍历的时候不把已经打上删除标记的点加到序列里去这样就彻底删了。
所以综上重构的条件是 过于不平衡、子树中删除的节点过多
pd函数是判断是否需要重构;
bool pd(int no)
{
return max(node[node[no].l].size, node[node[no].r].size) > phyz * node[no].size ||
node[no].size - node[no].fact > node[no].size * 0.3;
}
上面删除、添加里的check函数就是判断是否需要重构的函数了。
判断是否需要重构的时候应从根节点开始往下递归直到遇到不平衡子树或当前加的或删的这个节点为止。
void check(int& no,int end)
{
if(no == end)
return;
if(pd(no))
{
rebuild(no);
update(root,no);
return;
}
if(node[end].val < node[no].val)
{
check(node[no].l,end);
}
else
check(node[no].r,end);
}
update函数是用来更新重构子树 以上 的节点信息的(因为彻底删除了要删除的节点,所以size大小变化了,但是fact、实际的大小并没有变化;所以只用更新size的大小)。
void update(int no,int end)
{
if(!no)
return;
if(node[end].val < node[no].val)
update(node[no].l,end);
else
update(node[no