平衡树 treap 模板

#include
#include
#include
using namespace std;
//treap 就是tree+heap 利用了二叉堆的结构整体趋于平衡树(不一定是严格意义上的平衡)
//又利用了二叉平衡树的排序 才让查找 插入的效率在logn 
struct data{//该树的标号为完全二叉树标号方式 
    int l,r,v,size,rnd,w;//分别表示左右子树的序号,节点的值,
	//以该节点为根左右子树加上该点总共有多少值,节点的随机值(是建成二叉堆的依据),等于v的值有几个(因为可能有好几个相同的值) 
}tr[100005];
int n,size,root,ans;
void update(int k)//更新结点信息
{
    tr[k].size=tr[tr[k].l].size+tr[tr[k].r].size+tr[k].w;
}
void rturn(int &k)//参考左旋 
{
    int t=tr[k].l;tr[k].l=tr[t].r;tr[t].r=k;
    tr[t].size=tr[k].size;update(k);k=t;
}
void lturn(int &k)//左旋 
{
    int t=tr[k].r;
	tr[k].r=tr[t].l;//该点的右子树变成原来右子树的左子树 
	tr[t].l=k;//原来的右子树的左子树为该点 
    tr[t].size=tr[k].size;//原来右子树的大小就是为该点值的大小(好好考虑下 因为旋转了这棵树总的大小还是不变的) 
	update(k);//更新该点信息 
	k=t;//因为左旋后位置发生了改变 原来右子树的点到了该节点的位置 所以k赋值为t 
}
void insert(int &k,int x)//使用引用可直接赋值 
{     //插入肯定会最后被安排到叶子节点 
    if(k==0)//若已经到了叶子节点 
    {
        size++;k=size;//总节点数加一 给该节点标号 
        tr[k].size=tr[k].w=1;//该点的大小和w值都赋值为1 
		tr[k].v=x;
		tr[k].rnd=rand();//产生随机数并赋值 
        return;
    }
    tr[k].size++;//插入一个数 所查找经过的值都加一 因为从上往下走 一定是它的子孙会被插入添加 所以该节点也要修改 
    if(tr[k].v==x)tr[k].w++;//插入的数已经存在且就是该树 则直接在w上加一 
    else if(x>tr[k].v)//如果大于该节点值 
    {
        insert(tr[k].r,x);//向右寻找要插入的点 
        if(tr[tr[k].r].rnd1)//要删的值有好几个 
        {
            tr[k].w--;tr[k].size--;return;//直接把计数减少 
        }
        if(tr[k].l*tr[k].r==0)k=tr[k].l+tr[k].r;//如果该节点是只有一个子节点或者没有子节点 该节点直接被子节点替代 
        else if(tr[tr[k].l].rndtr[k].v)//查找 该路径上的值都要减一 
        tr[k].size--,del(tr[k].r,x);
    else tr[k].size--,del(tr[k].l,x);
}
int query_rank(int k,int x)//查询x的排名  就是把小于自己的树(左子树)的综合加上自身(加1) 
{
    if(k==0)return 0; 
    if(tr[k].v==x)return tr[tr[k].l].size+1;//找到了该节点 把左子树的个数加上自身的个数(只加一 因为只要求最小的那个值)  
    else if(x>tr[k].v)//如果大于该数将此节点的左子树和本身节点的w加起来再去右子树中找小于该数的数 
        return tr[tr[k].l].size+tr[k].w+query_rank(tr[k].r,x);
    else return query_rank(tr[k].l,x);//如果x小于当前节点从左子树找 
}
int query_num(int k,int x)//查询排名为x的数 
{
    if(k==0)return 0;
    if(x<=tr[tr[k].l].size)//如果x比左子树的总和小就往左子树里找 
        return query_num(tr[k].l,x);
    else if(x>tr[tr[k].l].size+tr[k].w)// 如果比该节点的左子树和该点w的总和大往右子树找第x-tr[tr[k].l].size-tr[k].w个值 
        return query_num(tr[k].r,x-tr[tr[k].l].size-tr[k].w);
    else return tr[k].v;//刚好缩到为1的时候的那个值就是查的那个值 
}
void query_pro(int k,int x)//查找该x的前驱 小于x的最大值 
{
    if(k==0)return;
    if(tr[k].vx)
    {
        ans=k;query_sub(tr[k].l,x);
    }
    else query_sub(tr[k].r,x);
}
int main()
{
    scanf("%d",&n);
    int opt,x;
    for(int i=1;i<=n;i++)
    {
        scanf("%d%d",&opt,&x);
        switch(opt)
        {
        case 1:insert(root,x);break; //root=1 
        case 2:del(root,x);break;
        case 3:printf("%d\n",query_rank(root,x));break;
        case 4:printf("%d\n",query_num(root,x));break;
        case 5:ans=0;query_pro(root,x);printf("%d\n",tr[ans].v);break;
        case 6:ans=0;query_sub(root,x);printf("%d\n",tr[ans].v);break;
        }
    }
    return 0;
}

参考:

https://www.cnblogs.com/MyStringIsNotNull/p/9165675.html 这里有很多图可以参考 特别是左右旋

https://baike.baidu.com/item/Treap/4321536?fr=aladdin

你可能感兴趣的:(平衡树 treap 模板)