FHQ_Treap笔记

#include <iostream>
#include <cstdio>
#include <time.h>
using namespace std;
const int N = 1E5;
int idx, root;
//FHQ_Treap核心思想:
//构建一个既有BST结构,又有堆结构的树
//通过以v为键值分裂,与 以key为键值合并 两个操作操作维护BST与堆结构
//从而用两个键值使树尽量分散,保证高效的搜索效率
//同时可以用val值作为关键分裂树,来插入、删除、查找结点,前驱,后驱,获取第k位排名的结点
struct node{
    int l, r;
    int val;
    int rnd;
    int size;
} tr[N];
void newnode(int&x,int v){
    x = ++idx;
    tr[x].val = v;
    tr[x].rnd = rand();
    tr[x].size = 1;
}
void pushup(int p){
    tr[p].size = tr[tr[p].l].size + tr[tr[p].r].size + 1;
}
//按值分裂
//把一棵树分为两棵树
//一棵树的val<=v
//另一棵树的val>v
//x为指向左Treap的指针
//y为指向右Treap的指针
void split(int p,int v,int &x,int &y){
    //如果p=0,递归到了空节点,返回
    if(!p){
        x = y = 0;
        return;
    }
    //如果当前结点p的val<=v
    //说明p及其左子树都属于分裂后的左Treap
    //而p的右子树部分可能也有<=v的结点
    //继续递归分裂右子树
    //把<=v的部分作为p的右子树
    //同时把x指向左Treap的根
    if(tr[p].val<=v){
        //x指向左Treap的根
        x = p;
        //递归分裂右子树
        //x变为了tr[x].r
        //用来找新的tr[x].r
        split(tr[x].r, v, tr[x].r, y);
        pushup(x);
    }
    else{
        y = p;
        split(tr[y].l, v, x, tr[y].l);
        pushup(y);
    }
}
//合并
//对两棵树
//按照key值合并
//我们保证前面这棵子树的valmax<=后面这棵子树的valmin
//所以只能用分裂出来的树进行合并
//同时我们保证合并的两棵树为堆的结构
//这样子,我们可以保证根据key值合并以后的树,它的val值仍然保持BST结构
//我们只需要考虑把哪棵树放在上面
//哪棵树放在下面
//将key小的放在上面
//返回根的编号
int merge(int x,int y){
    //如果x树为空或y树为空
    //直接返回x树或y树的编号
    if(!x||!y) return x + y;
    //x树的rnd值比y树的ran值小
    //将y加入到x的右子树中
    //递归比较x的右儿子与y的rnd值大小
    //x是根,返回x树的编号
    if(tr[x].rnd<tr[y].rnd){
        //因为x的val值一定小于y的val值,
        //所以y树只能接到x的右子树上
        tr[x].r = merge(tr[x].r, y);
        pushup(x);
        return x;
    }
    //同理
    else{
        //由于y的val值一定大于x的val值
        //所以x树只能接到y的左子树上
        tr[y].l = merge(x, tr[y].l);
        pushup(y);
        return y;
    }
}
//插入
//按照插入值分裂为x,y两棵树
//再新建一个结点,把树合并回去
void insert(int v){
    int x, y, z;
    //按照v值分裂,x指向val<=v的树
    //y指向val>v的树
    split(root, v, x, y);
    //创建val值为v的新结点
    newnode(z, v);
    //把新结点和x,y合并
    root = merge(merge(x, z), y);
}
//删除
//和插入类似
//先按删除值v分裂为x,z两棵树
//则x的值<=val
//z的值>val
//再按删除值v-1把x分裂为x',y两棵树
//则x'的值<=val-1
//y的值>val-1且<=val——要么是val,要么y为空
//合并y的左右子树为y'
//(相当于删除根节点——一次只删除一个结点)
//最后合并x',y',z
void del(int v){
    int x, y, z;
    split(root, v, x, z);
    split(x, v - 1, x, y);
    y = merge(tr[y].l, tr[y].r);
    root = merge(merge(x, y), z);
}
//找第k大的节点
int get_k(int p,int k){
    //如果是自己
    if(k==tr[tr[p].l].size+1){
        return tr[p].val;
    }
    else if(k<=tr[tr[p].l].size){
        return get_k(tr[p].l, k);
    }
    else{
        k -= tr[tr[p].l].size + 1;
        return get_k(tr[p].r, k);
    }
}
//找val的排名
//按照val-1将树分裂为x和y
//则x<=val-1
//y>val
//那么x的size+1就是排名
//然后再把x和y合并
int getrank(int v){
    int x, y;
    split(root, v - 1, x, y);
    int ans = tr[x].size + 1;
    root = merge(x, y);
    return ans;
}
//找前驱:
//按照val-1分裂为x,y两棵树
//在x内找最大值输出,然后合并
int getpre(int v){
    int x, y, s, ans;
    split(root, v - 1, x, y);
    //x的值<=val-1
    //y的值>val
    //s为x树的大小,也就是前驱的排名
    s = tr[x].size;
    //找到排名为s的结点就是找到了前驱
    ans = get_k(x, s);
    root = merge(x, y);
    return ans;
}
//找后驱:
//按照val分裂为x,y两棵树,在y内找最小值输出,然后合并
int getnxt(int v){
    int x, y, ans;
    split(root, v, x, y);
    //最小值就是在y树中排名为1的结点
    ans = get_k(y, 1);
    root=merge(x,y);
    return ans;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值