简析平衡树(一)——替罪羊树 Scapegoat Tree

本文介绍了替罪羊树,一种简单的平衡树数据结构。内容包括替罪羊树的基本概念,插入、删除、重构操作的详细解释,以及相关的代码实现。通过重构过程的暴力特点,展示了替罪羊树如何维护平衡。
摘要由CSDN通过智能技术生成
前言

平衡树在我的心目中,一直都是一个很高深莫测的数据结构。不过,由于最近做的题目的题解中经常出现“平衡树”这三个字,我决定从最简单的替罪羊树开始,好好学习平衡树。


简介

替罪羊树,英文名 S c a p e g o a t   T r e e Scapegoat\ Tree Scapegoat Tree,是我认为平衡树中最简单的一种。

替罪羊树可以当作一棵非常暴力二叉搜索树,因为它除了在子树不平衡时会暴力重构(不然为什么叫它平衡树)以外几乎和BST没有任何区别。


替罪羊树的基础操作
  • 插入

    不得不说,替罪羊树的插入操作简直与BST一模一样。
    直接上代码:

inline void Insert(int &x,int val)//插入操作
{
   
    if(!x)//如果当前节点为空,那么就将元素插入这个节点
    {
   
        x=Void[tot--],node[x].Val=val,node[x].Exist=1,Build(x);
        return;
    }
    ++node[x].Size,++node[x].Fac;//将这个子树的大小加1
    if(val<=node[x].Val) Insert(node[x].Son[0],val);//比较插入元素与当前元素,若小于等于当前元素,就插入到当前元素的左子树
    else Insert(node[x].Son[1],val);//否则,就插入到当前元素的右子树
} 
  • 删除

    替罪羊树的删除操作就很值得一提了。

    在删除替罪羊树上的一个元素时,我们并不会将其暴力删除(虽然替罪羊树在重构时非常暴力,但它的暴力是有选择性的,不然复杂度还不上天),而是标记这个节点不存在,并在计算它所在子树大小时将实际大小减1。这个思想是非常实用的,在许多地方我们都会用到。

    代码如下:

inline void Delete(int &x,int rk)//删除排名为rk的数
{
   
    if(node[x].Exist&&!((node[node[x].Son[0]].Fac+1)^rk))//如果当前节点存在(没有被删除)且刚好排名为rk,我们就将其删除
    {
   
        node[x].Exist=0,--node[x].Fac;//标记其不存在,并将该子树的实际大小减1
        return;
    }
    --node[x].Fac;//因为该子树中将有元素被删除,所以将该子树的实际大小减1
    if(node[node[x].Son[0]].Fac+node[x].Exist>=rk) Delete(node[x].Son[0],rk);//比较删除元素与当前元素的大小,若小于等于当前元素,就说明要删除的元素在当前元素的左子树
    else Delete(node[x].Son[1],rk-node[x].Exist-node[node[x].Son[0]].Fac);//否则说明要删除的元素在当前元素的右子树
}
inline void del(int v)//删除值为v的数
{
   
    Delete(rt,get_rank(v));//删除值为v的数,就相当于删除排名为值为v的数的排名的数,是不是有点绕?
    if((double)node[rt].Size*alpha>(double)node[rt].Fac) ReBuild(rt);//如果当前子树的实际大小小于该子树的大小乘以alpha(一般来说,取alpha=0.75),就重构该子树
}
  • 重构

    呃,接下来到了最关键的部分:重构。

    替罪羊树的重构真的是非常暴力。我们可以形象地理解它:

这里写图片描述

假设上图是一棵需要重构的子树(圆圈中是节点编号而不是节点权值)。

那么,我们就先非常暴力地将其拍扁:
这里写图片描述

然后,再将它以最中间的节点为新的根,重新拎起来:
这里写图片描述

重构就完成了。是不是一个极其暴力的过程?

代码如下:

inline void Traversal(int x)//拍扁原子树(中序遍历原子树,这样可以保证遍历后得到的元素是从大到小排序的)
{
   
    if(!x) return;//如果当前节点是空节点,就退出函数
    Traversal(node[x].Son[0]);//由于是中序遍历,所以先遍历该节点的左子树
    if(node[x].Exist) cur[++cnt]=x;//如果该节点存在,就将其加入数组
    else Void<
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值