重量平衡树之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 }

原文地址:https://www.cnblogs.com/aininot260/p/9330967.html

时间: 2024-10-11 09:43:37

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

关于平衡树(Treap)

平衡树是什么? 其实平衡树就是支持旋转的二叉查找树. 什么是二叉查找树呢? 其实就是满足(左子树(全部节点) < 根 < 右子树(全部节点))的一棵树,比如↓ (注意并不是每个节点都要是满的,也就是说它不一定是一棵完全二叉树) 那么二叉查找树有什么用呢? 假如我们要查询第k大的数,我们从根节点开始往下走,假如左子树大小大于等于k,我们就进入左子树去找:如果左子树大小等于(k-1),就输出当前节点:假如左子树大小小于(k-1),我们就进入右子树找排名为(k-(左子树大小+1))的数. 这样由于树

数组splay ------ luogu P3369 【模板】普通平衡树(Treap/SBT)

二次联通门 : luogu P3369 [模板]普通平衡树(Treap/SBT) #include <cstdio> #define Max 100005 #define Inline __attri\ bute__( ( optimize( "-O2" ) ) ) Inline void read (int &now) { now = 0; register char word = getchar (); bool temp = false; while (wor

替罪羊树 ------ luogu P3369 【模板】普通平衡树(Treap/SBT)

二次联通门 : luogu P3369 [模板]普通平衡树(Treap/SBT) 闲的没事,把各种平衡树都写写 比较比较... 下面是替罪羊树 #include <cstdio> #include <vector> #define Max_ 100010 #define Inline __attri\ bute__( ( optimize( "-O2" ) ) ) Inline void read (int &now) { register char w

红黑树 ------ luogu P3369 【模板】普通平衡树(Treap/SBT)

二次联通门 : luogu P3369 [模板]普通平衡树(Treap/SBT) 近几天闲来无事...就把各种平衡树都写了一下... 下面是红黑树(Red Black Tree) #include <cstdio> #define Max 100001 #define Red true #define Black false #define Inline __attri\ bute__( ( optimize( "-O2" ) ) ) Inline void read (i

P3369 【模板】普通平衡树(Treap/SBT)

二次联通门 : P3369 [模板]普通平衡树(Treap/SBT) /* luogu P3369 [模板]普通平衡树(Treap/SBT) splay 模板 支持插入x数 删除x数(若有多个相同的数,因只删除一个) 查询x数的排名(若有多个相同的数,因输出最小的排名) 查询排名为x的数 求x的前驱(前驱定义为小于x,且最大的数) 求x的后继(后继定义为大于x,且最小的数) */ #include <cstdio> #define Max 200005 void read (int &

AC日记——【模板】普通平衡树(Treap/SBT) 洛谷 P3369

[模板]普通平衡树(Treap/SBT) 思路: 劳资敲了一个多星期: 劳资终于a了: 劳资一直不a是因为一个小错误: 劳资最后看的模板: 劳资现在很愤怒: 劳资不想谈思路!!! 来,上代码: #include <cstdio> using namespace std; #define maxn 1000005 struct SplayTreeNodeType { int w,key,opi,size,ch[2]; }; struct SplayTreeNodeType tree[maxn];

[bzoj3065] 带插入区间第k小值 [重量平衡树套线段树]

题面 传送门 思路 发现强制在线了...... 本来可以树套树解决的问题,现在外层不能使用线段树了,拿什么替代呢? 我们需要一种支持单点插入.下套数据结构.数据结构上传合并复杂度最多单log,不能旋转的数据结构 这不是摆明了用重量平衡树吗? 我选了替罪羊树作为上层结构,下面套了一棵线段树,就做完了 查询的时候把替罪羊树上对应的log个区间提取出来,一起在底层权值线段树上二分即可 详见代码注释 Code #include<iostream> #include<cstdio> #inc

平衡树初阶——AVL平衡二叉查找树+三大平衡树(Treap + Splay + SBT)模板【超详解】

平衡树初阶——AVL平衡二叉查找树 一.什么是二叉树 1. 什么是树. 计算机科学里面的树本质是一个树状图.树首先是一个有向无环图,由根节点指向子结点.但是不严格的说,我们也研究无向树.所谓无向树就是将有向树的所有边看成无向边形成的树状图.树是一种递归的数据结构,所以我们研究树也是按照递归的方式去研究的. 2.什么是二叉树. 我们给出二叉树的递归定义如下: (1)空树是一个二叉树. (2)单个节点是一个二叉树. (3)如果一棵树中,以它的左右子节点为根形成的子树都是二叉树,那么这棵树本身也是二叉

三大平衡树(Treap + Splay + SBT)总结+模板

Treap树 核心是 利用随机数的二叉排序树的各种操作复杂度平均为O(lgn) Treap模板: #include <cstdio> #include <cstring> #include <ctime> #include <iostream> #include <algorithm> #include <cstdlib> #include <cmath> #include <utility> #include