普通平衡树(Fhq treap 非旋平衡树)

这里想要深度学习Fhq treap,推荐通过b站学习,B站大佬 不分解的AgOH 讲的非常好,建议观看学习
大佬讲解视频
题目描述

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

  1. 插入x数

  2. 删除x数(若有多个相同的数,因只删除一个)

  3. 查询x数的排名(若有多个相同的数,因输出最小的排名)

  4. 查询排名为x的数

  5. 求x的前驱(前驱定义为小于x,且最大的数)

  6. 求x的后继(后继定义为大于x,且最小的数)

输入描述

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

输出描述

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

Sample Input

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

Sample Output:

106465
84185
492737  

具体代码

#include <iostream>
#include<stdio.h>
#include<algorithm>
#include<map>
#include<random>
std::mt19937 rnd(233);
#define M 100010
using namespace std;
struct Node
{
    int left,right;
    int value,key;//值加索引
    int size;//子树大小
}tree[M];
int cnt,node;
int x,y,z;
inline int newnode(int val)//建立新节点和节点信息的保存
{
    tree[++cnt].value=val;
    tree[cnt].key=rnd();
    tree[cnt].size=1;
    return cnt;
}
void update(int now)//更新子树的大小
{
    tree[now].size=tree[tree[now].left].size+tree[tree[now].right].size+1;
}
void split(int now,int val,int& x,int& y)//x,y作为;两个返回值
{
    if(!now)//如果当前你分裂的数不存在,返回两个不存在的数
        x=y=0;
    else
    {
        if(tree[now].value<=val)
        {
            x=now;//如果根节点比val小左子树肯定比val小
            split(tree[now].right,val,tree[now].right,y);//对右子树进行递归操作
        }
        else
        {
            y=now;//如果根节点比val大右子树肯定比val大
            split(tree[now].left,val,x,tree[now].left);//对左子树进行递归操作
        }
        update(now);
    }
}
int merge(int x,int y)//合并
{
    if(!x||!y)//如果xy有一个不存在那就返回另一个
    {
        return x+y;
    }
    if(tree[x].key>tree[y].key)//如果x节点的索引大于y节点的索引,父节点的优先级肯定大于所有子节点的优先级,那么合并的话y肯定在x的右下方
    {//索引大说明x在y的上方
        tree[x].right=merge(tree[x].right,y);
        update(x);
        return x;
    }
    else
    {//x在y的左下方
        tree[y].left=merge(x,tree[y].left);
        update(y);
        return y;
    }
}
void ins(int val)//插入
{
    split(node,val,x,y);//以val为中间值分裂
    node=merge(merge(x,newnode(val)),y);//先合并x和val,在与y合并
    //printf("%dm\n",node);
}
void del(int val)//删除
{
    split(node,val,x,z);//按val把树分为x,z
    split(x,val-1,x,y);//按val-1把x分为x,y,此时y中值全部等于val
    y=merge(tree[y].left,tree[y].right);//删除y根节点的那个val,方法是将y等于y的左子树和右子树合并
    node=merge(merge(x,y),z);
}
void getrank(int val)//查询val的排名
{
    split(node,val-1,x,y);//此时x中全部为小于val的值
    printf("%d\n",tree[x].size+1);//x子树的大小加1就是val的排名
    node=merge(x,y);//合并回去

}
void getnum(int rank)
{
    int now=node;
    while(now)
    {
        if(tree[tree[now].left].size+1==rank)
            break;
        else if(tree[tree[now].left].size>=rank){
                now=tree[now].left;
        }
        else
        {
            rank-=tree[tree[now].left].size+1;
            now=tree[now].right;
        }
    }
    printf("%d\n",tree[now].value);
}
void pre(int val)//找前驱
{
    split(node,val-1,x,y);//以val-1分裂
    int now=x;//x最右侧的值就是前驱
    while(tree[now].right)//如果x里最右边不为空就继续找
    {
        now=tree[now].right;
    }
    printf("%d\n",tree[now].value);
    node=merge(x,y);
}
void nxt(int val)//找后继
{
    //printf("%d%d %d\n",x,y,node);
    split(node,val,x,y);//以val分裂
    int now=y;//y最左侧的值就是后继
    while(tree[now].left)//如果y的最左侧不为空就继续找
    {
        now=tree[now].left;
    }
    printf("%d\n",tree[now].value);
    node=merge(x,y);
}
int main()
{
    int n;
    scanf("%d",&n);
    while(n--)
    {
        int m,k;
        //printf("%d%d\n",x,y);
        scanf("%d%d",&m,&k);
        if(m==1)
        {
           ins(k);
           continue;
        }
        if(m==2)
        {
            del(k);
            continue;
        }
        if(m==3)
        {
            getrank(k);
            continue;
        }
        if(m==4)
        {
            getnum(k);
            continue;
        }
        if(m==5)
        {
            pre(k);
            continue;
        }
        if(m==6)
        {
            nxt(k);
            continue;
        }
    }
    return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值