P3369 【模板】普通平衡树(替罪羊树)

题目链接https://www.luogu.com.cn/problem/P3369
题目描述

您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作:

插入 x 数
删除 x 数(若有多个相同的数,因只删除一个)
查询 x 数的排名(排名定义为比当前数小的数的个数 +1 )
查询排名为 x 的数
求 x 的前驱(前驱定义为小于 x,且最大的数)
求 x 的后继(后继定义为大于 x,且最小的数)
输入格式

第一行为 n,表示操作的个数,下面 n 行每行有两个数 opt 和 x,\opt 表示操作的序号( 1≤opt≤6 )

输出格式

对于操作 3,4,5,6每行输出一个数,表示对应答案

Intput

10
1 106465
4 1
1 317721
1 460929
1 644985
1 84185
1 89851
6 81968
1 492737
5 493598

Output

106465
84185
492737

对于 100%的数据,1≤n≤10 5,∣x∣≤107

基于树的查找法是将待查表组织成特定树的形式并在树结构上实现查找的方法,故又称为树表式查找法,主要包括二叉排序树、平衡二叉树和B树。


  1. 替罪羊树满足二叉排序树(二叉搜索树)的结构特点
    二叉排序树
    若左子树非空,则左子树上所有结点的值均小于根结点的值。
    若右子树非空,则右子树上所有结点的值均大于(或大于等于)根结点的值。
    它的左右子树也分别为二叉排序树
    替罪羊树满足平衡二叉排序树的结构特点
    平衡二叉排序树
    左子树与右子树的高度之差的绝对值小于等于1
    左子树和右子树也是平衡二叉排序树

  1. 替罪羊树的基本操作: 增、删、查、重构
    插入x
    删除x
    查询x的排名
    查询排名为x的数
    求x的前驱,后继

  1. 核心操作:重构
    (1)失衡的判断: 宏定义alpha,若以**x为根的树的尺寸乘以alpha大于max(左子树的尺寸,右子树的尺寸)**则进行重构。

在这里插入图片描述
(2)将整棵树压缩为一条链(这条链为这颗失衡树的中序遍历)
在这里插入图片描述
(3)以中间的结点mid为根,重新构造二叉树
在这里插入图片描述


  1. 删除的特别性:标记
    在进行删除操作时,并不是真的删除结点,而是引入del标记,del
    为1表示这个结点存在,del为0表示这个结点不存在

    删除结点时,树的尺寸对应的也要发生变化,引入tot、Size分别表示子树的实际大小和初始大小。
    tot包含:未删除的结点+删除的结点
    Size包含:未删除的结点

  1. 内存池存储未使用的结点标号提高效率
for(int i=1000000; i>=1; i--)
       stk[++p]=i;///stk:下标从1开始,stk[1]存储的结点编号为100000

1.插入x

bool is_build(int x)
{
    if((double)tree[x].Size*alpha<=(double)max(tree[tree[x].l].Size,tree[tree[x].r].Size))
        return true;
    return false;
}
void Inorderdfs(int now)
{
    if(now!=0)
    {
        Inorderdfs(tree[now].l);
        if(tree[now].del)///此结点存在就存入中序遍历的数组,不存在就存入内存池
            inorder[++cnt]=now;
        else
            stk[++p]=now;
        Inorderdfs(tree[now].r);
    }
}
void build(int l,int r,int &now)
{
    int mid=(l+r)>>1;
    now=inorder[mid];
    if(l==r)
    {
        tree[now].l=0,tree[now].r=0;
        tree[now].Size=1,tree[now].tot=1;
        tree[now].del=1;
        return;
    }
    if(l<mid)
        build(l,mid-1,tree[now].l);
    else
        tree[now].l=0;
    build(mid+1,r,tree[now].r);
    tree[now].Size=tree[tree[now].l].Size+tree[tree[now].r].Size+1;
    tree[now].tot=tree[tree[now].l].tot+tree[tree[now].r].tot+1;
}
void rebuild(int &now)///引用
{
    cnt=0;
    Inorderdfs(now);///中序遍历
    if(cnt)
        build(1,cnt,now);
    else
        now=0;
}
void Insert(int &now,int val)///root是引用,形参变化,实参也变化
{
    if(now==0)
    {
        now=stk[p--];///新开辟一个结点
        tree[now].val=val;
        tree[now].l=0,tree[now].r=0;
        tree[now].Size=1,tree[now].tot=1;
        tree[now].del=1;
        return;
    }
    tree[now].Size++;
    tree[now].tot++;
    if(tree[now].val>val)
        Insert(tree[now].l,val);
    else
        Insert(tree[now].r,val);
    if(is_build(now))///是否需要重构
        rebuild(now);
}
int main()
{
	Insert(root,x);///插入x,每次都从根节点开始
}

2.删除x

int find_rank(int k)
{
    int now=root;
    int ans=1;
    while(now)
    {
        if(tree[now].val>=k)
            now=tree[now].l;
        else
        {
            ans+=tree[tree[now].l].Size+tree[now].del;///左子树的尺寸+自身
            now=tree[now].r;
        }
    }
    return ans;
}
void delet(int &now,int k)
{
    if(tree[now].del&&tree[tree[now].l].Size+1==k)
    {
        tree[now].del=0;///删除这个结点
        tree[now].Size--;///对应的子树的实际大小减1
        return;
    }
    tree[now].Size--;
    if(tree[tree[now].l].Size+tree[now].del>=k)///左子树的大小已经超过k
        delet(tree[now].l,k);///从左子树的左子树继续找
    else
        delet(tree[now].r,k-tree[tree[now].l].Size-tree[now].del);
}
void Delet(int k)
{
    delet(root,find_rank(k));///找出k的排名rank,删除排名为rank的数
    if((double)tree[root].tot*alpha>=(double)tree[root].Size)
        rebuild(root);
}
int main()
{
	Delet(x);///删除x
}

完整代码

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1e6+10;
#define alpha 0.75///重构因子
struct Treapbst
{
    int l,r;
    int val;///权值
    int Size,tot;///子树的实际大小(未删除的结点) 子树的大小(未删除的结点+删除的结点)
    int del;///删除标志 删除为0,否则为1
} tree[N];
int inorder[N],stk[N],root,p,cnt;///中序遍历   内存池:存储无用的结点编号
bool is_build(int x)
{
    if((double)tree[x].Size*alpha<=(double)max(tree[tree[x].l].Size,tree[tree[x].r].Size))
        return true;
    return false;
}
void Inorderdfs(int now)
{
    if(now!=0)
    {
        Inorderdfs(tree[now].l);
        if(tree[now].del)
            inorder[++cnt]=now;
        else
            stk[++p]=now;
        Inorderdfs(tree[now].r);
    }
}
void build(int l,int r,int &now)
{
    int mid=(l+r)>>1;
    now=inorder[mid];
    if(l==r)
    {
        tree[now].l=0,tree[now].r=0;
        tree[now].Size=1,tree[now].tot=1;
        tree[now].del=1;
        return;
    }
    if(l<mid)
        build(l,mid-1,tree[now].l);
    else
        tree[now].l=0;
    build(mid+1,r,tree[now].r);
    tree[now].Size=tree[tree[now].l].Size+tree[tree[now].r].Size+1;
    tree[now].tot=tree[tree[now].l].tot+tree[tree[now].r].tot+1;
}
void rebuild(int &now)///引用
{
    cnt=0;
    Inorderdfs(now);///中序遍历
    if(cnt)
        build(1,cnt,now);
    else
        now=0;
}
void Insert(int &now,int val)///root是引用,形参变化,实参也变化
{
    if(now==0)
    {
        now=stk[p--];///新开辟一个结点
        tree[now].val=val;
        tree[now].l=0,tree[now].r=0;
        tree[now].Size=1,tree[now].tot=1;
        tree[now].del=1;
        return;
    }
    tree[now].Size++;
    tree[now].tot++;
    if(tree[now].val>val)
        Insert(tree[now].l,val);
    else
        Insert(tree[now].r,val);
    if(is_build(now))///是否需要重构
        rebuild(now);
}
int find_rank(int k)
{
    int now=root;
    int ans=1;
    while(now)
    {
        if(tree[now].val>=k)
            now=tree[now].l;
        else
        {
            ans+=tree[tree[now].l].Size+tree[now].del;///左子树的尺寸+自身
            now=tree[now].r;
        }
    }
    return ans;
}
void delet(int &now,int k)
{
    if(tree[now].del&&tree[tree[now].l].Size+1==k)
    {
        tree[now].del=0;///删除这个结点
        tree[now].Size--;///对应的子树的实际大小减1
        return;
    }
    tree[now].Size--;
    if(tree[tree[now].l].Size+tree[now].del>=k)///左子树的大小已经超过k
        delet(tree[now].l,k);///从左子树的左子树继续找
    else
        delet(tree[now].r,k-tree[tree[now].l].Size-tree[now].del);
}
void Delet(int k)
{
    delet(root,find_rank(k));///找出k的排名rank,删除排名为rank的数
    if((double)tree[root].tot*alpha>=(double)tree[root].Size)
        rebuild(root);
}
int find_kth(int k)///查询排名为x的数
{
    int now=root;
    while(now)
    {
        if(tree[now].del&&tree[tree[now].l].Size+1==k)
            return tree[now].val;
        else if(tree[tree[now].l].Size>=k)
            now=tree[now].l;
        else
        {
            k-=tree[tree[now].l].Size+tree[now].del;
            now=tree[now].r;
        }
    }
    return tree[now].val;
}
int main()
{
    int t;
    scanf("%d",&t);
    for(int i=1000000; i>=1; i--)
        stk[++p]=i;///stk:下标从1开始,stk[1]存储的结点编号为100000
    while(t--)
    {
        int op,x;
        scanf("%d%d",&op,&x);
        if(op==1)
            Insert(root,x);///插入x,每次都从根节点开始
        if(op==2)
            Delet(x);///删除x
        if(op==3)
            printf("%d\n",find_rank(x));
        if(op==4)
            printf("%d\n",find_kth(x));
        if(op==5)///求x的前驱
            printf("%d\n",find_kth(find_rank(x)-1));///找到x的排名rank,再找排名为rank-1的数
        if(op==6)
            printf("%d\n",find_kth(find_rank(x+1)));///x+1可以表示为大于x且最小的数
    }
    return 0;
}

学习才是消除无聊,感受充实最好的办法

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zaiyang遇见

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值