平衡二叉树-FHQ Treap(无旋平衡树)c/c++代码实现

参考视频:https://www.bilibili.com/video/BV1ft411E7JW?t=1780
代码中有较为详细的注释

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

int cnt,root;
mt19937 rnd(233);  //随机数
struct Node{
    int l,r,val,key,size;
}fhq[100010];
int newnode(int val){  //开辟新结点
    fhq[++cnt].val=val;
    fhq[cnt].key=rnd();
    fhq[cnt].size=1;
    return cnt;   //直接返回一个新结点
}

void update(int now){  //push_up更新节点大小(当加入了新结点)
    fhq[now].size=fhq[fhq[now].l].size+fhq[fhq[now].r].size+1;
}
void spilt(int now,int val,int &x,int &y){ //分裂乘两棵树
    if(!now) x=y=0;  //如果是一颗空树,直接返回x,y是空树即可
    else{
        if(fhq[now].val<=val){  //这里是按值分裂把小于等于val值得分到x树,大于val得分到y树
            x=now; //这个值左孩子上得所有结点都属于x这棵树
            spilt(fhq[now].r,val,fhq[now].r,y);
            //右孩子上大部分都会大于这个值,
            //也有可能会有小于val这个值得,所以我们要继续递归右子树进行查找
        }
        else{
            y=now;   //复读机
            spilt(fhq[now].l,val,x,fhq[now].l);
        }
        update(now);
    }
}
int merge(int x,int y){   //把两棵树合并起来
    if(!x||!y) return x+y;
    if(fhq[x].key>fhq[y].key){   //既要符合堆得性质,又要符合搜索树得性质
        //我们知道x这棵树上得值全都小于y这颗树上得值,
        // 所以当x得key大于y这个key得时候,y在x得右下方(既要在右边,也要在下面)
        //所以让x得右子树跟y合并
        fhq[x].r=merge(fhq[x].r,y);
        update(x);
        return x;
    }
    else{
        fhq[y].l=merge(x,fhq[y].l);
        update(y);
        return y;
    }
}
int x,y,z;
void insert(int val){  //插入某值
    spilt(root,val,x,y);  //先把树按照val值分裂开
    //因为x树都小于等于val,所以我们直接让新结点跟x合并起来
    //再把x树跟y树合并起来
    root=merge(merge(x,newnode(val)),y);
}
void del(int val){ //删除(此del不可以删除树上没有得值)
    //先按照val将树分裂开
    //再把x树按照val-1分裂成x,y树
    //那么y树上所有得值都等于val
    spilt(root,val,x,z);
    spilt(x,val-1,x,y);
    //直接把y得根节点去掉即可,也就是把左右子树合并
    y=merge(fhq[y].l,fhq[y].r);
    root=merge(merge(x,y),z);//最后把他们合并起来即可
}
int get_rank(int val){  //排名
    spilt(root,val-1,x,y);
    //按照val-1把树分为xy,那么x上树所有值都小于val
    //那么x树上得根节点得size即为比他小得数得个数,再+1即可
    int temp=fhq[x].size+1;
    root=merge(x,y); //不要忘记把树还原
    //cout<<fhq[x].size<<endl;
    return temp;

}
int get_num(int rank){
    int now=root;
    while (now){  
        if(fhq[fhq[now].l].size+1==rank) break;  //如果找到直接返回结点值
            //如果搜索过得数大于rank,那么我们要往左数搜索
            //因为右子树的值已经大于rank了,所以答案一定不在右子树上
        else if(fhq[fhq[now].l].size>=rank) now=fhq[now].l;
        //及时减去已经走过的排名,类似于主席树第k大思想
        else{
            rank-=fhq[fhq[now].l].size+1;
            now=fhq[now].r;
        }
    }
    return fhq[now].val;
};

int pre(int val){   
    //找前驱,按照val-1分裂此树
    //在他前面的哪个数,一定是在x树上的最右端(最大值)
    spilt(root,val-1,x,y);
    int now=x;
    while (fhq[now].r) now=fhq[now].r;
    int temp=fhq[now].val;
    root=merge(x,y);
    return temp;
}
int nxt(int val){
    //找后继,按照val分裂此树
    //在他后面的树一定是y树上最左边的值(最小值)
    spilt(root,val,x,y);
    int now=y;
    while (fhq[now].l) now=fhq[now].l;
    int temp=fhq[now].val;
    root=merge(x,y);
    return temp;
}

void check(int now){  //中序遍历
    if(!now) return;
    check(fhq[now].l);
    printf("%d ",fhq[now].val);
    check(fhq[now].r);
}
int main() {
    int n;
    cin>>n;
    while (n--){
        int opt,x;
        scanf("%d%d",&opt,&x);
        if(opt==1) insert(x);
        if(opt==2) del(x);
        if(opt==3) printf("%d\n",get_rank(x));
        if(opt==4) printf("%d\n",get_num(x));
        if(opt==5) printf("%d\n",pre(x));
        if(opt==6) printf("%d\n",nxt(x));
//        check(root);
//        cout<<endl;
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/xxxxxiao123/article/details/107150599