fhqTreap小结

神奇数据结构------fhqTreap

fhqTreap是不需要旋转的平衡树,比splay常数小,代码短,还支持区间操作,而且

可持久化!!!

首先,结点的值val满足是一棵排序二叉树,节点的一个随机值key满足是一个堆(孩子的key比父亲大),因为是随机的,所以整棵树的高度接近于lg n,拆分合并也都是接近O(n lg n)

fhqTreap主要就是两个操作 其他都是无脑拆分合并:

1.split(拆分)

就是把根为now的一棵树拆为根为x的和根为y的两棵树,满足子树x的值都<=k,子树y的值都>k

void split(int now, int &x, int &y, int k) {
    if(!now) {x=y=0; return;} //如果带拆分的树为空,拆出来的两棵树也肯定为空
    if(tr[now].val<=k) x=now, split(tr[now].r, tr[x].r, y, k); //如果根节点的val<=k,那么左子树的val也就肯定都<=k,因为是排序二叉树,然后x=now,再把now的右子树分成两棵树,val<=k的那颗直接接到x的右子树上(因为x的左子树的val一定都<=k)
        else y=now, split(tr[now].l, x, tr[y].l, k); //否则根节点的val肯定>k,那么右子树的val也就肯定都>k,因为是排序二叉树,然后y=now,再把now的左子树分成两棵树,val>k的那颗直接接到y的左子树上(因为y的右子树的val一定都>k)
    update(now); //更新一下子树大小(后面会用到)
}

2.merge(合并)

就是把以x为根的树与以y为根的树按key合并,根节点为now

void merge(int &now, int x, int y) {
    if(x==0||y==0) {now=x+y; return;} //如果有一颗树为空,那么合并后就是另外一颗树,x+y如果一个为0,那么结果就是另一个
    if(tr[x].key<tr[y].key) now=x, merge(tr[now].r, tr[x].r, y); //如果x的key比y小,那么now的左子树=x的左子树(因为x的key比y小,所以x要当y的祖先),now的右子树是x的右子树和y合并的
        else now=y, merge(tr[now].l, x, tr[y].l); //否则x的key比y大,那么now的右子树=y的右子树(因为x的key比y大,所以y要当x的祖先),now的左子树是y的左子树和x合并的
    update(now); //更新一下子树大小(后面会用到)
}

然后就是一大波操作。。。。。

查找

int find(int now, int rank) { //在根为now的子树找第rank小的
    while(tr[tr[now].l].si+1!=rank) //左子树大小+1是根节点的排名
        if(tr[tr[now].l].si>=rank) now=tr[now].l; //左子树里找第rank小的
            else rank-=tr[tr[now].l].si+1, now=tr[now].r; //右子树里找第rank-tr[tr[now].l].si+1小的,因为去掉左子树和根
    return now; 
}

插入

void ins(int d) { //插入值为d的节点
    int x, y, z=New(d); //新建一个值为d的节点赋给z,z是待插入的结点
    split(rt, x, y, d); //把rt(整棵树)拆分成<=d和>d的两颗子树
    merge(x, x, z); //x和z合并
    merge(rt, x, y); //此时x包含了z,再把x和y合并,就插入了一个新节点
}

删除

void del(int d) { //删除值为d的节点
    int x, y, z;
    split(rt, x, y, d);
    split(x, x, z, d-1); //此时z是值为d的所有节点
    merge(z, tr[z].l, tr[z].r); //把z的左子树和右子树合并,相当于把根删掉
    merge(x, x, z);
    merge(rt, x, y); //合并 
}

…剩下的很简单,就看下代码吧P3369 【模板】普通平衡树

#include<cstdio>        
#include<cstring>
#include<cstdlib>
#include<ctime> 
using namespace std;
const int maxn=100020, INF=(int)(2.1e9), mod=1000007;
struct pj{
    int si, val, key, l, r;
} tr[maxn]; int len, rt=1;
int read() {
    int x=0, f=1; char c=getchar();
    for (; c<'0'||c>'9'; c=getchar()) if(c=='-') f=0;
    for (; c>='0'&&c<='9'; c=getchar()) x=(x<<3)+(x<<1)+c-'0';
    return f?x:-x; 
}
int Random() {return (long long)rand()*rand()%mod;}
int New(int val) {
    tr[++len].val=val; tr[len].si=1; tr[len].key=Random(); tr[len].l=tr[len].r=0; return len;
}
void update(int x) {tr[x].si=tr[tr[x].l].si+tr[tr[x].r].si+1;}
void split(int now, int &x, int &y, int k) {
    if(!now) {x=y=0; return;}
    if(tr[now].val<=k) x=now, split(tr[now].r, tr[x].r, y, k);
        else y=now, split(tr[now].l, x, tr[y].l, k); 
    update(now);
}
void merge(int &now, int x, int y) {
    if(x==0||y==0) {now=x+y; return;}
    if(tr[x].key<tr[y].key) now=x, merge(tr[now].r, tr[x].r, y);
        else now=y, merge(tr[now].l, x, tr[y].l);
    update(now);
}
int find(int now, int rank) {
    while(tr[tr[now].l].si+1!=rank)
        if(tr[tr[now].l].si>=rank) now=tr[now].l;
            else rank-=tr[tr[now].l].si+1, now=tr[now].r;
    return now; 
}
void ins(int d) {
    int x, y, z=New(d);
    split(rt, x, y, d);
    merge(x, x, z);
    merge(rt, x, y);
}
void del(int d) {
    int x, y, z;
    split(rt, x, y, d);
    split(x, x, z, d-1);
    merge(z, tr[z].l, tr[z].r);
    merge(x, x, z);
    merge(rt, x, y); 
}
void find_pm(int d) {
    int x, y;
    split(rt, x, y, d-1);
    printf("%d\n", tr[x].si+1);
    merge(rt, x, y);
}
void find_sz(int k) {
    printf("%d\n", tr[find(rt, k)].val);
}
void find_qq(int d) {
    int x, y;
    split(rt, x, y, d-1);
    printf("%d\n", tr[find(x, tr[x].si)].val);
    merge(rt, x, y);
}
void find_hj(int d) {
    int x, y;
    split(rt, x, y, d);
    printf("%d\n", tr[find(y, 1)].val);
    merge(rt, x, y);
}
int main() {
    srand((unsigned)time(0));
    int n; n=read(); New(INF); tr[1].si=0;
    for (int ty, x; n--; ) {
        ty=read(); x=read(); 
        switch(ty) {
            case 1: ins(x); break;
            case 2: del(x); break;
            case 3: find_pm(x); break;
            case 4: find_sz(x); break;
            case 5: find_qq(x); break;
            case 6: find_hj(x); break;
        }
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值