数据结构之fhq-treap

Posted xxzh

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据结构之fhq-treap相关的知识,希望对你有一定的参考价值。

本文内容部分转自某大佬博客:https://blog.csdn.net/CABI_ZGX/article/details/79963427

例题:https://www.luogu.org/problemnew/show/P3369#sub

题目描述

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

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

输入输出格式

输入格式:

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

输出格式:

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

 

看题目就知道Treap当然是可以的,考虑fhq-treap

fhq-treap的最大特点,不需要不需要不需要旋转!!!这是棵不考旋转保持平衡的平衡树

不管怎么样,我们要让它是平衡的。好的其实有些除旋转之外的神奇的操作是有效的,下面来讲一讲。

对于随机数我们维护小根堆;对于权值左儿子的权值小于当前节点的权值,右儿子的权值大于当前节点的权值

Mergy

merge就是把两个treap合成一个,是一个递归的过程。首先明确fhq-treap的平衡条件是他的附加权值,那么只需要比较他们的附加权值大小判断在左树还是右树即可。 

注意x树里的所有点的权值小于y树里的所有点的权值

inline int mergy(int x,int y)
{
    if (!x||!y) return x|y;
    if (a[x].rnd<a[y].rnd)
    {
        a[x].son[1]=mergy(a[x].son[1],y);
        update(x);
        return x;    
    }
    else 
    {
        a[y].son[0]=mergy(x,a[y].son[0]);
        update(y);
        return y;
    }
}

Split

split是把一颗treap分开两个树的操作,也是一个递归的过程。有两种分法,一种是按权值分,一种是按size(子树大小)分。

按权值分(注意这时候权值小于等于 k的节点都在左树中,大于k的都在右树中)

void split(int now,int k,int &x,int &y)
{
    if (!now) 
    {
        x=y=0;
        return;
    }
    if (k>=a[now].dat) 
    {
        x=now;
        split(a[now].son[1],k,a[now].son[1],y);    
    }
    else 
    {
        y=now;
        split(a[now].son[0],k,x,a[now].son[0]);    
    }
    update(now);
    return;
}

按size分

void split(int now,int k,int &x,int &y)
{
    if(!now) x=y=0;
    else
    {
        update(now);
         if (k<=tr[tr[now].son[0]].size)
            y=now,split(tr[now].son[0],k,x,tr[now].son[0]);
        else
            x=now,split(tr[now].son[1],k-tr[tr[now].son[0]].size-1,tr[now].son[1],y);
        update(now);
    }
}

New,建立新的节点

int New(int x)
{
    tot++;
    a[tot].dat=x;a[tot].rnd=rand();a[tot].size=1;
    return tot;
}

找kth的节点的权值

int kth(int now,int k)
{
    while (1)
    {
        if (k<=a[a[now].son[0]].size) now=a[now].son[0];
        else 
        {
            if (k==a[a[now].son[0]].size+1) return now;
            else
            {
                k=k-(a[a[now].son[0]].size+1);
                now=a[now].son[1];    
            }    
        }    
    }
}

Insert

插入一个权值为t节点,那么只需要以t为权值split整棵树,然后New(t)在merge回去

split(root,t,x,y);
root=mergy(mergy(x,New(t)),y);

Del

删除权值为t的点,先把整颗树以t为权值split成两棵树x,z,再把x树按照t-1分成x,y。这时候值为t的点一定为y的根,那么我们把y的两个子儿子merge起来,再把他们重新merge起来得到一个新的树,这颗树就去除掉了t。

split(root,t,x,z);    
split(x,t-1,x,y);
y=mergy(a[y].son[0],a[y].son[1]);
root=mergy(mergy(x,y),z);

FindRank

找值为t的节点排名,就把整棵树以t-1为权值split,那么小于t的节点全在左树内,输出左树的size即可

precursor

找前驱就把整棵树按t-1为权值split开,此时小于等于t的数全在左树里,在左树里找到排名为(左树的size)的节点权值即可。

split(root,t-1,x,y);    
printf("%d
",a[kth(x,a[x].size)].dat);
root=mergy(x,y);

successor

找后继是相同的,把整棵树按t为权值split开,此时右树排名第一的数就是后继

split(root,t,x,y);    
printf("%d
",a[kth(y,1)].dat);
root=mergy(x,y);

 

fhq-treap代码:

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<time.h>
using namespace std;

const int maxn=1e5+15;
int n,tot,root;
struct fhq
{
    int son[2];
    int rnd,dat,size;
}a[maxn];
inline int read()
{
    char ch=getchar();
    int s=0,f=1;
    while (!(ch>=0&&ch<=9)) {if (ch==-) f=-1;ch=getchar();}
    while (ch>=0&&ch<=9) {s=(s<<3)+(s<<1)+ch-0;ch=getchar();}
    return s*f;
}
inline void update(int x)
{
    a[x].size=a[a[x].son[0]].size+a[a[x].son[1]].size+1;
    return;
}
int New(int x)
{
    tot++;
    a[tot].dat=x;a[tot].rnd=rand();a[tot].size=1;
    return tot;
}
inline int mergy(int x,int y)
{
    if (!x||!y) return x|y;
    if (a[x].rnd<a[y].rnd)
    {
        a[x].son[1]=mergy(a[x].son[1],y);
        update(x);
        return x;    
    }
    else 
    {
        a[y].son[0]=mergy(x,a[y].son[0]);
        update(y);
        return y;
    }
}
void split(int now,int k,int &x,int &y)
{
    if (!now) 
    {
        x=y=0;
        return;
    }
    if (k>=a[now].dat) 
    {
        x=now;
        split(a[now].son[1],k,a[now].son[1],y);    
    }
    else 
    {
        y=now;
        split(a[now].son[0],k,x,a[now].son[0]);    
    }
    update(now);
    return;
}
int kth(int now,int k)
{
    while (1)
    {
        if (k<=a[a[now].son[0]].size) now=a[now].son[0];
        else 
        {
            if (k==a[a[now].son[0]].size+1) return now;
            else
            {
                k=k-(a[a[now].son[0]].size+1);
                now=a[now].son[1];    
            }    
        }    
    }
}
int main()
{
    srand(time(0));
    n=read();
    while (n--)
    {
        int opt=read(),t=read();
        int x,y,z;
        if (opt==1)
        {
            split(root,t,x,y);
            root=mergy(mergy(x,New(t)),y);
        }    
        if (opt==2)
        {
            split(root,t,x,z);    
            split(x,t-1,x,y);
            y=mergy(a[y].son[0],a[y].son[1]);
            root=mergy(mergy(x,y),z);
        }
        if (opt==3)
        {
            split(root,t-1,x,y);
            printf("%d
",a[x].size+1);
            root=mergy(x,y);
        }
        if (opt==4)
        {
            int k=kth(root,t);    
            printf("%d
",a[k].dat);
        }
        if (opt==5)
        {
            split(root,t-1,x,y);    
            printf("%d
",a[kth(x,a[x].size)].dat);
            root=mergy(x,y);
        }
        if (opt==6)
        {
            split(root,t,x,y);    
            printf("%d
",a[kth(y,1)].dat);
            root=mergy(x,y);
        }
    }
    return 0;
}

Treap代码:

#include<bits/stdc++.h>
using namespace std;

const int maxn=1e5+15;
const int inf=1e9+7;
int n,m,tot,root;
struct TREAP
{
    int l,r;
    int val,dat;
    int cnt,size;
}a[maxn];
inline void read(int &x)
{
    char ch=getchar();
    int s=0,f=1;
    while (!(ch>=0&&ch<=9)) {if (ch==-) f=-1;ch=getchar();}
    while (ch>=0&&ch<=9) {s=(s<<3)+(s<<1)+ch-0;ch=getchar();}
    x=s*f;
}
int New(int val){
    a[++tot].val=val;
    a[tot].cnt=a[tot].size=1;
    a[tot].dat=rand();
    return tot;
}
void update(int p){
    a[p].size=a[a[p].l].size+a[p].cnt+a[a[p].r].size;
}
void build()
{
    New(-inf);New(inf);
    root=1;a[1].r=2;
    update(root);
}
int get_rank_by_val(int p,int val)
{
    if (!p) return 0;
    if (val==a[p].val) return a[a[p].l].size+1;
    if (val<a[p].val) return get_rank_by_val(a[p].l,val);
    return a[a[p].l].size+a[p].cnt+get_rank_by_val(a[p].r,val);
}
int get_val_by_rank(int p,int rank)
{
    if (!p) return inf;
    if (a[a[p].l].size>=rank) return get_val_by_rank(a[p].l,rank);
    if (a[a[p].l].size+a[p].cnt>=rank) return a[p].val;
    return get_val_by_rank(a[p].r,rank-a[a[p].l].size-a[p].cnt);
}
void zig(int &p)
{
    int q=a[p].l;
    a[p].l=a[q].r;a[q].r=p;p=q;
    update(a[p].r);update(p);
}
void zag(int &p)
{
    int q=a[p].r;
    a[p].r=a[q].l;a[q].l=p;p=q;
    update(a[p].l);update(p);
}
void insert(int &p,int val)
{
    if (!p){
         p=New(val);
         return;
    }
    if (val==a[p].val){
        a[p].cnt++;update(p);
        return;
    }
    if (val<a[p].val){
        insert(a[p].l,val);
        if (a[p].dat<a[a[p].l].dat) zig(p);
    }
    else {
        insert(a[p].r,val);
        if (a[p].dat<a[a[p].r].dat) zag(p);
    }
    update(p);
}
int getpre(int val)
{
    int ans=1;
    int p=root;
    while (p){
        if (val==a[p].val){
            if (a[p].l>0) {
            p=a[p].l;
            while (a[p].r>0) p=a[p].r;
            ans=p;
            }
            break;
        }
        if (a[p].val<val&&a[p].val>a[ans].val) ans=p;
        if (val<a[p].val) p=a[p].l;else p=a[p].r;
    }
    return a[ans].val;
}
int getnext(int val)
{
    int ans=2;
    int p=root;
    while (p){
        if (val==a[p].val){
            if (a[p].r>0){
            p=a[p].r;
            while (a[p].l>0) p=a[p].l;
            ans=p;
            }
            break;
        }
        if (a[p].val>val&&a[p].val<a[ans].val) ans=p;
        if (val<a[p].val) p=a[p].l;else p=a[p].r;
    }
    return a[ans].val;
}
void remove(int &p,int val)
{
    if (!p) return;
    if (val==a[p].val){
        if (a[p].cnt>1) {
            a[p].cnt--;update(p);
            return;
        }
        if (a[p].l||a[p].r){
            if (a[p].r==0||a[a[p].l].dat>a[a[p].r].dat){
                zig(p);remove(a[p].r,val);
            }
            else {
                zag(p);remove(a[p].l,val);
            }
            update(p);
        }
        else p=0;
        return;
    }
    if (val<a[p].val) remove(a[p].l,val);else remove(a[p].r,val);
    update(p);
}
int main()
{
    int opt;
    build();
    read(n);
    while (n--)
    {
        read(opt);int x;
        read(x);
        if (opt==1) insert(root,x); 
        if (opt==2) remove(root,x);
        if (opt==3) printf("%d
",get_rank_by_val(root,x)-1);
        if (opt==4) printf("%d
",get_val_by_rank(root,x+1));
        if (opt==5) printf("%d
",getpre(x));
        if (opt==6) printf("%d
",getnext(x));
    }
    return 0;
}

其实还有vector写法:

#include<bits/stdc++.h>
using namespace std;

int n,opt,x;
vector <int> p;
int main()
{
    p.reserve(100001+15);
    scanf("%d",&n);
    while (n--)
    {
    scanf("%d%d",&opt,&x);
    if (opt==1) p.insert(lower_bound(p.begin(),p.end(),x),x);
    if (opt==2) p.erase(lower_bound(p.begin(),p.end(),x));
    if (opt==3) printf("%d
",lower_bound(p.begin(),p.end(),x)-p.begin()+1);
    if (opt==4) printf("%d
",p[x-1]);
    if (opt==5)printf("%d
",p[lower_bound(p.begin(),p.end(),x)-p.begin()-1]);
    if (opt==6) printf("%d
",p[upper_bound(p.begin(),p.end(),x)-p.begin()]);
    }
    return 0;
}

 

以上是关于数据结构之fhq-treap的主要内容,如果未能解决你的问题,请参考以下文章

模板fhq-treap

fhq-Treap 文艺平衡树代码记录

fhq-Treap原理

fhq-treap

FHQ-treap模板篇

Fhq-Treap 学习笔记