重量平衡树之Treap:以随机优先级来维护堆结构,并满足BST性质

关于重量平衡树的相关概念可以参考姊妹文章:重量平衡树之替罪羊树

Treap是依靠旋转来维护平衡的重量平衡树中最为好写的一中,因为它的旋转不是LL就是RR

对于每一个新的节点,它给这个节点分配了一个随机数,用作优先级,然后以这个优先级来维护一个堆结构

由于堆本身就是完全二叉树结构,这样维护之后的树就无限接近于完全二叉树,所以还是很神奇的

这棵树满足BST的一切性质,除了不能处理序列问题之外已经无敌了

应该说,抛去动态树问题之外,这是实战最好用的树了

我们还是先看定义:

struct Tree
{
    int v,w;
    int size;
    int rnd;
    int ch[2];
}t[maxn];
int root;
int size;
int ans=0;

在这里v是值,w是同值的节点个数,size是子树的节点总数,rnd是优先级,外面:root是根节点,size是根节点中元素个数,ans是统计答案用的临时变量

我们这里还是先介绍插入操作,平衡树问题如果不是处理序列的,建议就一个一个插

void insert(int &k,int x)
{
    if(k==0)
    {
        size++;
        k=size;
        t[k].size=t[k].w=1;
        t[k].v=x;
        t[k].rnd=rand();
        return;
    }
    t[k].size++;
    if(t[k].v==x)
        t[k].w++;
    else if(x>t[k].v)
    {
        insert(t[k].ch[1],x);
        if(t[t[k].ch[1]].rnd<t[k].rnd)
            lturn(k);
    }
    else
    {
        insert(t[k].ch[0],x);
        if(t[t[k].ch[0]].rnd<t[k].rnd)
            rturn(k);
    }
}

插入时根据是否是叶子节点,遍历到的节点的w值等进行维护

每次插入要判断一下是否满足堆结构,进行相应的旋转调整

下面给出旋转调整的函数,基本上可以作为左旋和右旋的模板了

void rturn(int &k)
{
    int tmp=t[k].ch[0];
    t[k].ch[0]=t[tmp].ch[1];
    t[tmp].ch[1]=k;
    t[tmp].size=t[k].size;
    update(k);
    k=tmp;
}
void lturn(int &k)
{
    int tmp=t[k].ch[1];
    t[k].ch[1]=t[tmp].ch[0];
    t[tmp].ch[0]=k;
    t[tmp].size=t[k].size;
    update(k);
    k=tmp;
}

然后我们给出update函数,这里要维护的东西很少,只有一个size,所以这个时候的update就是更新size用的

void update(int k)
{
    t[k].size=t[t[k].ch[0]].size+t[t[k].ch[1]].size+t[k].w;
}

然后是四种基本查询工作,各种平衡树基本一致,也可以作为模板记下来了

int query_rank(int k,int x)
{
    if(k==0)
        return 0;
    if(t[k].v==x)
        return t[t[k].ch[0]].size+1;
    else if(x>t[k].v)
        return t[t[k].ch[0]].size+t[k].w+query_rank(t[k].ch[1],x);
    else 
        return query_rank(t[k].ch[0],x);
}
int query_num(int k,int x)
{
    if(k==0)
        return 0;
    if(x<=t[t[k].ch[0]].size)
        return query_num(t[k].ch[0],x);
    else if(x>t[t[k].ch[0]].size+t[k].w)
        return query_num(t[k].ch[1],x-t[t[k].ch[0]].size-t[k].w);
    else
        return t[k].v;
}
void query_pro(int k,int x)
{
    if(k==0)
        return;
    if(t[k].v<x)
        ans=k,query_pro(t[k].ch[1],x);
    else
        query_pro(t[k].ch[0],x);
}
void query_sub(int k,int x)
{
    if(k==0)
        return;
    if(t[k].v>x)
        ans=k,query_sub(t[k].ch[0],x);
    else
        query_sub(t[k].ch[1],x);
}

最后我们给出完整的模板,这棵树一定要熟练掌握,只要是平衡树问题,很大可能都是用它来完成的

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cstdlib>
  4 using namespace std;
  5 const int maxn=100005;
  6 int n;
  7 struct Tree
  8 {
  9     int v,w;
 10     int size;
 11     int rnd;
 12     int ch[2];
 13 }t[maxn];
 14 int root;
 15 int size;
 16 int ans=0;
 17 void update(int k)
 18 {
 19     t[k].size=t[t[k].ch[0]].size+t[t[k].ch[1]].size+t[k].w;
 20 }
 21 void rturn(int &k)
 22 {
 23     int tmp=t[k].ch[0];
 24     t[k].ch[0]=t[tmp].ch[1];
 25     t[tmp].ch[1]=k;
 26     t[tmp].size=t[k].size;
 27     update(k);
 28     k=tmp;
 29 }
 30 void lturn(int &k)
 31 {
 32     int tmp=t[k].ch[1];
 33     t[k].ch[1]=t[tmp].ch[0];
 34     t[tmp].ch[0]=k;
 35     t[tmp].size=t[k].size;
 36     update(k);
 37     k=tmp;
 38 }
 39 void insert(int &k,int x)
 40 {
 41     if(k==0)
 42     {
 43         size++;
 44         k=size;
 45         t[k].size=t[k].w=1;
 46         t[k].v=x;
 47         t[k].rnd=rand();
 48         return;
 49     }
 50     t[k].size++;
 51     if(t[k].v==x)
 52         t[k].w++;
 53     else if(x>t[k].v)
 54     {
 55         insert(t[k].ch[1],x);
 56         if(t[t[k].ch[1]].rnd<t[k].rnd)
 57             lturn(k);
 58     }
 59     else
 60     {
 61         insert(t[k].ch[0],x);
 62         if(t[t[k].ch[0]].rnd<t[k].rnd)
 63             rturn(k);
 64     }
 65 }
 66 void del(int &k,int x)
 67 {
 68     if(k==0)
 69         return;
 70     if(t[k].v==x)
 71     {
 72         if(t[k].w>1)
 73         {
 74             t[k].w--;
 75             t[k].size--;
 76             return;
 77         }
 78         if(t[k].ch[0]*t[k].ch[1]==0)
 79             k=t[k].ch[0]+t[k].ch[1];
 80         else if(t[t[k].ch[0]].rnd<t[t[k].ch[1]].rnd)
 81             rturn(k),del(k,x);
 82         else
 83             lturn(k),del(k,x);
 84     }
 85     else if(x>t[k].v)
 86         t[k].size--,del(t[k].ch[1],x);
 87     else
 88         t[k].size--,del(t[k].ch[0],x);
 89 }
 90 int query_rank(int k,int x)
 91 {
 92     if(k==0)
 93         return 0;
 94     if(t[k].v==x)
 95         return t[t[k].ch[0]].size+1;
 96     else if(x>t[k].v)
 97         return t[t[k].ch[0]].size+t[k].w+query_rank(t[k].ch[1],x);
 98     else 
 99         return query_rank(t[k].ch[0],x);
100 }
101 int query_num(int k,int x)
102 {
103     if(k==0)
104         return 0;
105     if(x<=t[t[k].ch[0]].size)
106         return query_num(t[k].ch[0],x);
107     else if(x>t[t[k].ch[0]].size+t[k].w)
108         return query_num(t[k].ch[1],x-t[t[k].ch[0]].size-t[k].w);
109     else
110         return t[k].v;
111 }
112 void query_pro(int k,int x)
113 {
114     if(k==0)
115         return;
116     if(t[k].v<x)
117         ans=k,query_pro(t[k].ch[1],x);
118     else
119         query_pro(t[k].ch[0],x);
120 }
121 void query_sub(int k,int x)
122 {
123     if(k==0)
124         return;
125     if(t[k].v>x)
126         ans=k,query_sub(t[k].ch[0],x);
127     else
128         query_sub(t[k].ch[1],x);
129 }
130 int main()
131 {
132     cin>>n;
133     int tmp,x;
134     for(int i=1;i<=n;i++)
135     {
136         cin>>tmp>>x;
137         switch(tmp)
138         {
139             case 1:insert(root,x);break;
140             case 2:del(root,x);break;
141             case 3:cout<<query_rank(root,x)<<endl;break;
142             case 4:cout<<query_num(root,x)<<endl;break;
143             case 5:ans=0;query_pro(root,x);cout<<t[ans].v<<endl;break;
144             case 6:ans=0;query_sub(root,x);cout<<t[ans].v<<endl;break;
145         }
146     }
147     return 0;
148 }

猜你喜欢

转载自www.cnblogs.com/aininot260/p/9330967.html
今日推荐