旋转Treap

Treap是什么

  树堆,在数据结构中也称 T r e a p Treap Treap,是指有一个随机附加域满足堆的性质的二叉搜索树,其结构相当于以随机数据插入的二叉搜索树。其基本操作的期望时间复杂度为 O ( l o g n ) O(logn) O(logn)。相对于其他的平衡二叉搜索树, T r e a p Treap Treap的特点是实现简单,且能基本实现随机平衡的结构。
   T r e a p Treap Treap是一棵二叉排序树,它的左子树和右子树分别是一个 T r e a p Treap Treap,和一般的二叉排序树不同的是, T r e a p Treap Treap纪录一个额外的数据,就是优先级。 T r e a p Treap Treap在以关键码构成二叉排序树的同时,还满足堆的性质(在这里我们假设父节点优先级的值要小于孩子节点优先级的值)。但是这里要注意的是 T r e a p Treap Treap和二叉堆有一点不同,就是二叉堆必须是完全二叉树,而 T r e a p Treap Treap可以并不一定是。

基本结构

class Treap
{
public:
    int rt;
private:
    int ans,sz;
    vector<int> lson,rson,val,rnd,siz,eql;
}

   r t rt rt代表 T r e a p Treap Treap的树根,是一个非负整数,0代表空树。
   a n s ans ans在查找前驱后继时有用,详见后文。
   s z sz sz等于整棵树的元素个数。
   l s o n i 、 r s o n i lson_i、rson_i lsonirsoni分别代表节点 i i i的左右儿子编号。
   v a l i 、 r n d i val_i、rnd_i valirndi分别代表节点 i i i的值 k e y key key和优先级 p r i o r i t y priority priority
   s i z i siz_i sizi等于以 i i i为根的 T r e a p Treap Treap的节点个数。
   e q l i eql_i eqli代表等于 v a l i val_i vali的元素个数。

基本操作

   T r e a p Treap Treap可以在 O ( h ) O(h) O(h)复杂度下实现以下操作:

  1. 插入数x。
  2. 删除数x,若有多个相同的x,则只删除1个。
  3. 查询数x的排名,若有多个相同的x,则输出最小的排名。举个例子,比如一共有1、2、3、3、5这五个数,则3的排名为3。
  4. 查询排名为x的数。
  5. 求x的前趋,即小于x且最大的数。
  6. 求x的后继,即大于x且最小的数。

  下面将一一讲解以上操作的实现以及一些辅助函数。

初始化

  此处 T r e a p Treap Treap的实现没有采用指针,所以应该在初始化时传入元素个数(实际上由于删除节点时没有回收编号,此处传入的值要自己好好算一下)以便于初始化 v e c t o r vector vector

Treap::Treap(int n):rt(0),sz(0),ans(0)
{
    ++n;
    lson.resize(n),rson.resize(n),val.resize(n),rnd.resize(n),siz.resize(n),eql.resize(n);
    srand(time(NULL));
}

修改某个节点的siz

  这是辅助函数之一,用来修改某个节点的 s i z siz siz,根据定义我们知道 s i z [ i ] = s i z [ l s o n [ i ] ] + s i z [ r s o n [ i ] ] + e q l [ i ] siz[i]=siz[lson[i]]+siz[rson[i]]+eql[i] siz[i]=siz[lson[i]]+siz[rson[i]]+eql[i]。即以 i i i为根的 T r e a p Treap Treap的元素个数等于左右子树元素个数之和加上这个节点的元素个数。

void Treap::modifySize(int idx)
{
    siz[idx]=siz[lson[idx]]+siz[rson[idx]]+eql[idx];
}

左旋

  话不多说,直接看图:
在这里插入图片描述

void Treap::leftRotate(int &idx)
{
    int r=rson[idx];
    rson[idx]=lson[r];
    lson[r]=idx;
    siz[r]=siz[idx];
    modifySize(idx);
    idx=r;
}

  注意最后一步 i d x = r idx=r idx=r,因为经过左旋后,新的根节点就是 r r r了,所以我们应该令 i d x = r idx=r idx=r(注意参数是一个引用)。

右旋

  和左旋恰好相反,借用一下百度百科的图吧:
在这里插入图片描述

void Treap::rightRotate(int &idx)
{
    int l=lson[idx];
    lson[idx]=rson[l];
    rson[l]=idx;
    siz[l]=siz[idx];
    modifySize(idx);
    idx=l;
}

插入数x

  这是一个递归操作,如果我们到达了叶子节点,那么就新建一个节点,并修改对应的值;否则如果要插入的数 x x x等于当前节点的值 v a l [ i ] val[i] val[i],那么直接递增 e q l [ i ] eql[i] eql[i]即可;若 x < v a l [ i ] x<val[i] x<val[i],将其插入到左子树中,插入操作完成后,需要判断左儿子和自己的优先级,若不满足则需要进行一次右旋操作;若 x > v a l [ i ] x>val[i] x>val[i],操作和上面的类似,只不过要反着来。

void Treap::insert(int &idx,int v)
{
    if(!idx)
    {
        idx=++sz;
        siz[idx]=eql[idx]=1;
        val[idx]=v;
        rnd[idx]=rand();
        return;
    }
    siz[idx]++;
    if(val[idx]==v)
        ++eql[idx];
    else if(val[idx]<v)
    {
        insert(rson[idx],v);
        if(rnd[rson[idx]]<rnd[idx])
            leftRotate(idx);
    }
    else
    {
        insert(lson[idx],v);
        if(rnd[lson[idx]]<rnd[idx])
            rightRotate(idx);
    }
}

删除数x

  这也是一个递归操作,如果到达叶子节点,函数结束;如果要删除的数 x x x等于当前节点的值 v a l [ i ] val[i] val[i],且当 e q l [ i ] > 1 eql[i]>1 eql[i]>1,那么递减 s i z [ i ] 、 e q l [ i ] siz[i]、eql[i] siz[i]eql[i]然后函数结束,否则说明当前节点的 e q l = 1 eql=1 eql=1,我们需要删除这个节点,这时要讨论左右儿子的关系,如果左儿子为空,那么直接把右儿子提上来;如果右儿子为空,那么直接把左儿子提上来;否则需要比较左右儿子的优先级,把值较小的那个上来(通过左旋右旋),然后再进入对应的子树继续删除操作;如果 x < v a l [ i ] x<val[i] x<val[i],递减 s i z [ i ] siz[i] siz[i],进入左子树;如果 x > v a l [ i ] x>val[i] x>val[i],递减 s i z [ i ] siz[i] siz[i],进入右子树。
  从上述叙述可以看出,删除操作其实是通过不断的左旋右旋把目标节点旋转到叶节点上,然后将其删除

void Treap::del(int &idx,int v)
{
    if(!idx)
        return;
    if(val[idx]==v)
    {
        if(eql[idx]>1)
        {
            --siz[idx],--eql[idx];
            return;
        }
        if(!lson[idx]||!rson[idx])
            idx=lson[idx]+rson[idx];
        else if(rnd[lson[idx]]<rnd[rson[idx]])
        {
            rightRotate(idx);
            del(idx,v);
        }
        else
        {
            leftRotate(idx);
            del(idx,v);
        }
    }
    else if(val[idx]<v)
    {
        --siz[idx];
        del(rson[idx],v);
    }
    else
    {
        --siz[idx];
        del(lson[idx],v);
    }
}

查找数x的排名

  查找给定的数 x x x的排名。如果 x x x等于当前节点的值 v a l [ i ] val[i] val[i],那么它的排名就等于 s i z [ l s o n [ i ] ] + 1 siz[lson[i]]+1 siz[lson[i]]+1;如果 x < v a l [ i ] x<val[i] x<val[i],那么递归进入左子树查询;如果 x > v a l [ i ] x>val[i] x>val[i],那么它的排名等于 s i z [ l s o n [ i ] ] + s q l [ i ] + siz[lson[i]]+sql[i]+ siz[lson[i]]+sql[i]+查询右子树的返回值。

int Treap::queryRank(int idx,int v)
{
    if(!idx)
        return 0;
    if(val[idx]==v)
        return siz[lson[idx]]+1;
    else if(val[idx]<v)
        return siz[lson[idx]]+eql[idx]+queryRank(rson[idx],v);
    else
        return queryRank(lson[idx],v);
}

查找排名为x的数

  如果 x < = s i z [ l s o n [ i ] ] x<=siz[lson[i]] x<=siz[lson[i]],那么递归进入左子树查询;如果 x > s i z [ l s o n [ i ] ] + e q l [ i ] x>siz[lson[i]]+eql[i] x>siz[lson[i]]+eql[i],那么递归进入右子树查询,不过此时应该查询排名为 x − s i z [ l s o n [ i ] ] − e q l [ i ] x-siz[lson[i]]-eql[i] xsiz[lson[i]]eql[i]的数;否则说明 v a l [ i ] val[i] val[i]就是排名为 x x x的数。

int Treap::queryNum(int idx,int rk)
{
    if(!idx)
        return 0;
    if(rk<=siz[lson[idx]])
        return queryNum(lson[idx],rk);
    else if(rk>siz[lson[idx]]+eql[idx])
        return queryNum(rson[idx],rk-siz[lson[idx]]-eql[idx]);
    return val[idx];
}

查找数x的前趋

  当 v a l [ i ] < x val[i]<x val[i]<x时,一个可能的答案是 i i i,为了找到更大的答案,我们要进入右子树继续查找;当 v a l [ i ] > = x val[i]>=x val[i]>=x时, i i i以及它的右子树不满足题意,要进入左子树查找。

void Treap::_queryPre(int idx,int v)
{
    if(!idx)
        return;
    if(val[idx]<v)
    {
        ans=idx;
        _queryPre(rson[idx],v);
    }
    else
        _queryPre(lson[idx],v);
}

int Treap::queryPre(int idx,int v)
{
    ans=0;
    _queryPre(idx,v);
    return val[ans];
}

查找数x的后继

  当 v a l [ i ] > x val[i]>x val[i]>x时,一个可能的答案是 i i i,为了找到更小的答案,要进入左子树查找;当 v a l [ i ] < = x val[i]<=x val[i]<=x时, i i i以及它的左子树不满足题意,要进入右子树查找。

void Treap::_querySub(int idx,int v)
{
    if(!idx)
        return;
    if(val[idx]>v)
    {
        ans=idx;
        _querySub(lson[idx],v);
    }
    else
        _querySub(rson[idx],v);
}

int Treap::querySub(int idx,int v)
{
    ans=0;
    _querySub(idx,v);
    return val[ans];
}

完整模板

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;

class Treap
{
public:
    int rt;
    Treap(int n);
    void insert(int &idx,int v);
    void del(int &idx,int val);
    int queryRank(int idx,int v);
    int queryNum(int idx,int rk);
    int queryPre(int idx,int v);
    int querySub(int idx,int v);
private:
    int ans,sz;
    vector<int> lson,rson,val,rnd,siz,eql;
    void modifySize(int idx);
    void leftRotate(int &idx);
    void rightRotate(int &idx);
    void _queryPre(int idx,int v);
    void _querySub(int idx,int v);
};

Treap::Treap(int n):rt(0),sz(0),ans(0)
{
    ++n;
    lson.resize(n),rson.resize(n),val.resize(n),rnd.resize(n),siz.resize(n),eql.resize(n);
    srand(time(NULL));
}

void Treap::modifySize(int idx)
{
    siz[idx]=siz[lson[idx]]+siz[rson[idx]]+eql[idx];
}

void Treap::leftRotate(int &idx)
{
    int r=rson[idx];
    rson[idx]=lson[r];
    lson[r]=idx;
    siz[r]=siz[idx];
    modifySize(idx);
    idx=r;
}

void Treap::rightRotate(int &idx)
{
    int l=lson[idx];
    lson[idx]=rson[l];
    rson[l]=idx;
    siz[l]=siz[idx];
    modifySize(idx);
    idx=l;
}

void Treap::insert(int &idx,int v)
{
    if(!idx)
    {
        idx=++sz;
        siz[idx]=eql[idx]=1;
        val[idx]=v;
        rnd[idx]=rand();
        return;
    }
    siz[idx]++;
    if(val[idx]==v)
        ++eql[idx];
    else if(val[idx]<v)
    {
        insert(rson[idx],v);
        if(rnd[rson[idx]]<rnd[idx])
            leftRotate(idx);
    }
    else
    {
        insert(lson[idx],v);
        if(rnd[lson[idx]]<rnd[idx])
            rightRotate(idx);
    }
}

void Treap::del(int &idx,int v)
{
    if(!idx)
        return;
    if(val[idx]==v)
    {
        if(eql[idx]>1)
        {
            --siz[idx],--eql[idx];
            return;
        }
        if(!lson[idx]||!rson[idx])
            idx=lson[idx]+rson[idx];
        else if(rnd[lson[idx]]<rnd[rson[idx]])
        {
            rightRotate(idx);
            del(idx,v);
        }
        else
        {
            leftRotate(idx);
            del(idx,v);
        }
    }
    else if(val[idx]<v)
    {
        --siz[idx];
        del(rson[idx],v);
    }
    else
    {
        --siz[idx];
        del(lson[idx],v);
    }
}

int Treap::queryRank(int idx,int v)
{
    if(!idx)
        return 0;
    if(val[idx]==v)
        return siz[lson[idx]]+1;
    else if(val[idx]<v)
        return siz[lson[idx]]+eql[idx]+queryRank(rson[idx],v);
    else
        return queryRank(lson[idx],v);
}

int Treap::queryNum(int idx,int rk)
{
    if(!idx)
        return 0;
    if(rk<=siz[lson[idx]])
        return queryNum(lson[idx],rk);
    else if(rk>siz[lson[idx]]+eql[idx])
        return queryNum(rson[idx],rk-siz[lson[idx]]-eql[idx]);
    return val[idx];
}

void Treap::_queryPre(int idx,int v)
{
    if(!idx)
        return;
    if(val[idx]<v)
    {
        ans=idx;
        _queryPre(rson[idx],v);
    }
    else
        _queryPre(lson[idx],v);
}

int Treap::queryPre(int idx,int v)
{
    ans=0;
    _queryPre(idx,v);
    return val[ans];
}

void Treap::_querySub(int idx,int v)
{
    if(!idx)
        return;
    if(val[idx]>v)
    {
        ans=idx;
        _querySub(lson[idx],v);
    }
    else
        _querySub(rson[idx],v);
}

int Treap::querySub(int idx,int v)
{
    ans=0;
    _querySub(idx,v);
    return val[ans];
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值