替罪羊树

24 篇文章 0 订阅
1 篇文章 0 订阅

花了一个下午,总算是把替罪羊树学会了。
在一众平衡树里面,替罪羊树可谓是独树一帜233.
因为其他平衡树里面,基本思想都是通过旋转,更改节点关系来做到平衡树.
但我们的替罪羊树可不是纸样滴.
核心思想是,重建!!!
对,泥萌木有听错,是重建233.
如果一颗子树深度失衡,我们就可以用中序遍历的方法来得到一个有序序列。
然后,从中间把这个区间给提起来,从而做到深度最优。
感觉这个思想,贼暴力。其实还是很好滴。
对于平衡的定义,在替罪羊树中有一个判别公式
对于一个节点x,定义一个alpha(0.5<=alpha<=1),如果满足size(lson(x)<=size(x)∗alpha&&size(rson(x))<=size(x)∗alpha, 即两个子树的size都不超过以该节点为根的子树的size,那就称这个节点或这个子树是平衡的。
我们可以通过调整alpha的值,来控制我们拍扁区间的次数。
对于一颗搜索树而言,替罪羊树的确是比slpay要快不少。
这是Splay版的
Splay
替罪羊树版
这里写图片描述
洛谷的数据还不算太SX,在cd数据下,优势会更明显。
Q:那要Splay干啥!
A: 因为它功能多!(打脸
至于为啥叫替罪羊树?
我猜是删除操作时是靠替罪实现的
也有人说是因为一棵树因为一个节点不平衡而导致被拍扁,根节点当了替罪羊。
孰是孰非不可判233
这里给出裸题和代码,希望对各位OIER有帮助!
链接
裸码//博主写的比较丑

#include <cstdio>
#include <iostream>
#define db double
#define il inline 
using namespace std;
int inf=1<<30;
const int maxm=1e7+1;
double al=0.75;
struct node{
    int val,fa,size,son[2];
}t[maxm];
bool check_blance(int id)
{
    return (db)t[id].size*al>=(db)t[t[id].son[0]].size&&(db)t[id].size*al>=(db)t[t[id].son[1]].size;
}
int n,cnt,root;
int sum,reb[maxm];
il void recycle(int id)//拍扁序列,回收节点 
{
    if(t[id].son[0]) recycle(t[id].son[0]);
    reb[++sum]=id;
    if(t[id].son[1]) recycle(t[id].son[1]);
} 
il bool get(int id)//SX函数,毫无作用 
{
    int fat=t[id].fa;
    return t[fat].son[1]==id;
}
il int build(int l,int r)//建树 
{
    if(l>r) return 0;
    int mid=(l+r)>>1;
    int id=reb[mid];
    int ls=build(l,mid-1);
    int rs=build(mid+1,r);
    t[id].son[0]=ls,t[ls].fa=id;
    t[id].son[1]=rs,t[rs].fa=id;
    t[id].size=t[ls].size+t[rs].size+1;
    return id;
}
il void rebuild(int id)//重建树 
{
    sum=0;
    recycle(id);
    int fat=t[id].fa;
    bool f=get(id);
    int cur=build(1,sum);
    t[cur].fa=fat;
    t[fat].son[f]=cur;
    if(root==id) root=cur;//换一下根节点 
}
il void init()
{
  cnt=2,root=1;
  t[1].val=-inf,t[1].size=2,t[1].son[1]=2;
  t[2].val=inf,t[2].size=1,t[2].fa=1;
}
il void insert(int x)
{
    int now=root,cur=++cnt;
    t[cur].val=x,t[cur].size=1; 
    while(1)
    {
        bool f=(x>=t[now].val);
        t[now].size++;
        if(t[now].son[f]) now=t[now].son[f];
        else
        {
            t[now].son[f]=cur;
            t[cur].fa=now;
            break;
        }
    }
    int flag=0;
    for(int i=cur;i;i=t[i].fa) if(!check_blance(i)) flag=i;
    if(flag) rebuild(flag);
}
il int find_num(int x)//查找节点编号 
{
    int now=root;
    while(1)
    {
        if(t[now].val==x) return now;
        else now=(t[now].son[t[now].val<x]);
    }
} 
il void del(int id)
{
    if(t[id].son[0]&&t[id].son[1])
    {
        int cur=t[id].son[0];
        while(t[cur].son[1]) cur=t[cur].son[1];
        t[id].val=t[cur].val,id=cur;//替罪操作233 
    }//删除操作需要找到左子树的最后一个节点或右子树的第一个节点来顶替,优先找左子树
    int Son=0;
    if(t[id].son[0]) Son=t[id].son[0];
    else Son=t[id].son[1]; 
    int f=get(id),fat=t[id].fa;
    t[fat].son[f]=Son;
    t[Son].fa=fat;
    for(int i=fat;i;i=t[i].fa) t[i].size--;
    if(root==id) root=Son;
}
il int find_rank(int x)
{
    int now=root,ans=0;
    while(now)
    {
        if(t[now].val<x) ans+=t[t[now].son[0]].size+1,now=t[now].son[1];
        else now=t[now].son[0];
    }
    return ans;
}
il int find_kth(int k)
{
  int now=root;
  while(1){
    if(t[t[now].son[0]].size==k-1) return now;
    else if(t[t[now].son[0]].size>=k) now=t[now].son[0];
    else k-=t[t[now].son[0]].size+1,now=t[now].son[1];
  }
  return now;
}
il int get_front(int x)
{ 
  int now=root,ans=-inf;
  while(now)
  {
    if(t[now].val<x) ans=max(ans,t[now].val),now=t[now].son[1];
    else now=t[now].son[0];
  }
  return ans;
}
il int get_behind(int x)
{  
  int now=root,ans=inf;
  while(now)
  {
    if(t[now].val>x) ans=min(ans,t[now].val),now=t[now].son[0];
    else now=t[now].son[1];
  }
  return ans;
}
int main()
{
    init(); 
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        int opt,x;
        scanf("%d%d",&opt,&x);
        if(opt==1)
         insert(x);
        if(opt==2)
         {
            int k=find_num(x);
            del(k);
         }
        if(opt==3)
         printf("%d\n",find_rank(x));
        if(opt==4)
         printf("%d\n",t[find_kth(x+1)].val);
        if(opt==5)
         printf("%d\n",get_front(x));
        if(opt==6)
         printf("%d\n",get_behind(x));
    }

    return 0;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值