ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

treap(小根堆)模板

2022-07-10 15:33:37  阅读:175  来源: 互联网

标签:ch return int ll id treap 小根堆 节点 模板


总结教训

对于treap使用小根堆性质,一定要特判左右子树是否存在,因为空节点的优先级为0,是最高的,不特判会出错我就这么错了,so

一定要特判!一定要特判!一定要特判!重要的事情说三遍

本文代码根据P3369 【模板】普通平衡树 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)写的

模板,有注释:

//treap(小根堆性质) 
#include<bits/stdc++.h>
#define rint register int
typedef long long ll;
using namespace std;
inline ll read()//快读 
{
    ll x=0;
    bool fg=false;
    char ch=getchar();
    while(ch<'0'||ch>'9')
    {
        if(ch=='-')    fg=true;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9')
    {
        x=(x<<3)+(x<<1)+(ch^48);
        ch=getchar();
    }
    return fg?~x+1:x;
}
const int N=1e6+5;
const ll INF=1e9+5;
int n,tot,root;//n 操作次数 tot 记录节点的标号 root 根节点 
int ch[N][2];//左右孩子 
ll val[N],pai[N],cnt[N],siz[N];
//val 数值 pai 存储优先级 cnt 该数值出现了几次 siz 大小 
inline void pushup(int id)//更新siz 
{
    siz[id]=siz[ch[id][0]]+siz[ch[id][1]]+cnt[id];
    //更新,该节点的大小=左子树的大小+右子树的大小+自己出现了几次 
}
int New(ll v)//插入新节点 
{
    val[++tot]=v;
    pai[tot]=rand();
    //随机函数,随即赋予一个数值,头文件<stdlib.h>
    cnt[tot]++;//cnt[tot]=1;
    siz[tot]++;//siz[tot]=1;
    return tot;
}
void spin(int &id,int d)
//旋转 id 要旋转的节点编号,因为会改变,所以要加&  d(direction) 旋转方向 
{
    int temp=ch[id][d^1];//记录与旋转方向相反的子节点,这里画图自己理解一下 
    ch[id][d^1]=ch[temp][d];//先存下来
    ch[temp][d]=id;//再修改 
    pushup(id);//先更新原节点 
    pushup(temp);//再更新旋转后的节点 
    id=temp;//最后更新id,以便其他操作需要 
}
void build()
//初始化,这两个点一个在最左下角,一个在最右下角,这里最左下角的会影响后面有关排名的的查询 
{
    root=New(-INF);
    ch[root][1]=New(INF);
    if(pai[root]>pai[ch[root][1]])    spin(root,0);//不符合小根堆性质,就左旋 
}
void insert(int &id,ll x)//插入节点 
{
    if(id==0)//如果已经到叶子节点了,直接插入新节点 
    {
        id=New(x);
        return;
    }
    if(x==val[id])    cnt[id]++;//如果和该节点数值相等,cnt(出现次数)+1 
    else
    {
        int d=x<val[id]?0:1;//判断是属于左子树还是右子树 
        insert(ch[id][d],x);
        if(pai[ch[id][d]]<pai[id])    spin(id,d^1);//要符合小根堆的性质 
    }
    pushup(id);//更新大小 
}
void del(int &id,ll x)
{
    if(!id)    return;//如果没有这个点,直接返回
    //这里else不能少,因为id是会改变的 
    if(x==val[id])//就是该节点,进行特判
    {
        if(cnt[id]>1)    cnt[id]--;//出现多次,减一即可 
        else//这里小根堆一定要进行特判子树,因为空节点的优先级最高,会报错 
        {
            if(ch[id][0]&&!ch[id][1])//只有左子树,没有右子树 
                spin(id,1),del(ch[id][1],x);
            else
            {
                if(!ch[id][0]&&ch[id][1])//只有右子树,没有左子树 
                spin(id,0),del(ch[id][0],x);
                else
                {
                    if(ch[id][0]&&ch[id][1])//左右子树都有 
                    {
                        int d=pai[ch[id][0]]<pai[ch[id][1]]?1:0;//判断优先级 
                        spin(id,d);//旋转 
                        del(ch[id][d],x);//删除 
                    }
                    else    id=0;//左右子树都没有 
                }
            }
        }                
    }
    else//如果不是该节点,向子树出发 
    {
        int d=x<val[id]?0:1;
        del(ch[id][d],x);
    }
    pushup(id);//更新大小 
}
ll get_rank(int id,ll x)//找x数(这里是数字)的排名(注意,是排名) 
{
    if(!id)    return 0;//如果没有这个数,返回0 
    if(x<val[id])    return get_rank(ch[id][0],x);
    //比当前节点小,向左子树中找 
    if(x>val[id])    return siz[ch[id][0]]+cnt[id]+get_rank(ch[id][1],x);
    //比当前节点大,向右子树中找 
    return siz[ch[id][0]]+1;
    //不大不小,则就是该节点,要返回这个点左子树的大小,并加上自己(也就是+1) 
}
ll get_val(int id,ll x)//找排名为x的数(注意,是数字) 
{
    if(!id)    return 0;//如果该节点为空,返回0 
    if(x<=siz[ch[id][0]])    return get_val(ch[id][0],x);
    //如果排名小于等于左子树的大小,就说明排名的这个位置在左子树中 
    if(x>siz[ch[id][0]]&&x<=siz[ch[id][0]]+cnt[id])    return val[id];
    //如果大于左子树大小,小于等于左子树大小+当前节点的大小,则就是该节点 
    return get_val(ch[id][1],x-siz[ch[id][0]]-cnt[id]);
    //以上都不符合,就是在右子树,这里x要减去左子树大小和当前节点的出现次数 
}
ll get_pre(ll x)//求前驱 
{
    int id=root;
    ll pre;
    while(id)
    {
        if(val[id]<x)//如果当前节点比x小,记录答案,去右子树中搜更优值 
        {
            pre=val[id];//记录答案 
            id=ch[id][1];//id转到右子树 
        }
        else    id=ch[id][0];//否则,在左子树中搜 
    }
    return pre;
}
ll get_nxt(ll x)//求后继 
{
    int id=root;
    ll nxt;
    while(id)
    {
        if(val[id]>x)//如果当前节点比x大,记录答案,去左子树中搜更优值 
        {
            nxt=val[id];//记录答案 
            id=ch[id][0];//id转到左子树 
        }
        else    id=ch[id][1];//否则,在右子树中搜 
    }
    return nxt;
}
int main()
{
    build();//初始化 
    n=read();//读入操作次数 
    for(rint i=1;i<=n;++i)
    {
        int op=read();
        ll x=read();
        switch(op)
        {
            case 1:
                insert(root,x);
                break;
            case 2:
                del(root,x);
                break;
            case 3:
                printf("%lld\n",get_rank(root,x)-1);
                break;
            case 4:
                printf("%lld\n",get_val(root,x+1));
                break;
            case 5:
                printf("%lld\n",get_pre(x));
                break;
            case 6:
                printf("%lld\n",get_nxt(x));
                break;
        }
    }
    return 0;
}

大根堆模板链接:treap(大根堆)模板 - yi_fan0305 - 博客园 (cnblogs.com)

标签:ch,return,int,ll,id,treap,小根堆,节点,模板
来源: https://www.cnblogs.com/yifan0305/p/16463234.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有