数据结构 — FHQ Treap

数据结构 — FHQ Treap

FHQ Treap 是啥呢?

FHQ Treap 据说是一个叫 fhq 的奆佬在考场上因为忘记算法而灵机一动创造出来的数据结构

treap = tree + heap ,就是同时拥有二叉搜索树和堆性质的一个数据结构。FHQ Treap 和 Splay 及其他树堆的区别就在于,没有乱七八糟的旋转操作

机制

FHQ Treap 就是一个平衡一点的二叉搜索树,而平衡则是通过定义一个随机 priority 使它满足堆性质保持的(并且由于堆和二叉搜索树的良好性质,treap 的子树也是一个合法的 treap )。所以,FHQ Treap 的各种操作都是平均 O(log n) 的

 1 template<typename T,int maxsize>
 2 class Treap
 3 {
 4     public:
 5         Treap(){Seed=(int)(maxsize*999983ll%2147483647);clear();}
 6         void clear()
 7         {
 8             Total=Root=0;
 9             treap[0].Size=treap[0].Left=treap[0].Right=treap[0].Priority=0;
10         }
11         int create(T key)//initialize a node
12         {
13             int u=++Total;
14             treap[u].Size=1;
15             treap[u].Key=key;
16             treap[u].Left=treap[u].Right=0;
17             treap[u].Priority=Random();
18             return u;
19         }
20     private:
21         struct Node
22         {
23             T Key;
24             int Left,Right,Size,Priority;
25         }treap[maxsize];
26         int Seed,Total,Root;
27         int Random()
28         {
29             return Seed=(int)(Seed*943199ll%1000000007);
30         }
31 };
View Code

初始化大概就长这个样子(因为我不压行所以显得很长的亚子

下面就是 FHQ Treap 的重点了(敲黑板

FHQ Treap 的各种操作

Split

顾名思义,Split 就是把一个 treap 分成两个 treap 的操作。当然,新的 treap 也要有该满足的性质

Split 分为两种,一种是按照 size 分,一种是按照关键字分

void KeySplit(int u,T key,int &x,int &y)

把以结点 u 为根的 treap 按照关键字分成两个,储存在 x 和 y 里,x 里面关键字都小于等于 key ,y 里面都大于 key

对于结点 u ,若其关键字小于等于 key ,则根据二叉搜索树的性质,u 和 u 的左子树都应该属于 x ,不妨让 x 就等于 u ,x 的左子树就等于 u 的左子树(这样很方便的保证了 priority 满足堆的性质)。但是 x 的右子树是什么呢?我们可以发现,其实再把 u 的右子树按照 key 分成两个 treap ,小于等于 key 的那部分做 x 的右子树,大于 key 的那部分赋值给 y 即可。

u 的关键字大于 key 的情况,和上面的情形是对称的

注:代码里假设 0 为哨兵结点,即空结点

 1 void KeySplit(int u,T key,int &x,int &y)//Split the treap by key
 2 {
 3     if(!u)
 4     {
 5         x=y=0;
 6         return ;
 7     }
 8     if(treap[u].Key<=key)
 9     {
10         x=u;
11         KeySplit(treap[u].Right,key,treap[u].Right,y);
12     }
13     else
14     {
15         y=u;
16         KeySplit(treap[u].Left,key,x,treap[u].Left);
17     }
18     pushup(u);//pushup(u)和线段树以及其他数据结构类似,用来更新当前结点关键字
19 }
View Code

void SizeSplit(int u,int Size,int &x,int &y)

按照大小分类和按照关键字分类的机制差不多,代码也和上面的类似

 1 void SizeSplit(int u,int Size,int &x,int &y)//Split the treap by size
 2 {
 3     if(!u)
 4     {
 5         x=y=0;
 6         return ;
 7     }
 8     if(treap[treap[u].Left].Size<Size)
 9     {
10         x=u;
11         SizeSplit(treap[u].Right,Size-treap[treap[u].Left].Size-1,treap[u].Right,y);//一定不要忘了把左子树大小和根节点给减掉
12     }
13     else
14     {
15         y=u;
16         SizeSplit(treap[u].Left,Size,x,treap[u].Left);
17     }
18     pushup(u);
19 }
View Code

Merge

int merge(int x,int y)

将两个 treap 合成一个,返回根的结点序号

但是不是随便两个 treap 就能 merge 的,merge 要求 x 中所有结点 key 小于 y 中所有结点 key ,这样才能方便的保持二叉搜索树的性质

这时候,我们有两种 merge 方法都能满足二叉搜索树性质:把 y 合并到 x 右子树上 or 把 x 合并到 y 左子树上。考虑维护 priority 堆的性质,若 x 的优先级比 y 大,用第一种,反之,用第二种

 1 int merge(int x,int y)//guarantee that : all key in x < all key in y
 2 {
 3     if(!x||!y)//这里处理 x,y 中有空结点的情形
 4         return x+y;
 5     if(treap[x].Priority>treap[y].Priority)
 6     {
 7         treap[x].Right=merge(treap[x].Right,y);
 8         pushup(x);
 9         return x;
10     }
11     else
12     {
13         treap[y].Left=merge(x,treap[y].Left);
14         pushup(y);
15         return y;
16     }
17 }
View Code

一些询问

Treap 的所有 query & modification 都是通过 split 和 merge 实现的

删除元素:将 treap 分为小于 key ,等于 key ,大于 key 三部分,用 1,3 两部分 merge 成新的 treap

添加元素:将 treap 分成小于、大于等于两部分,按序 merge 小于、新建的结点、大于等于

查找关键字排名:将 treap 分成小于、大于等于两部分,返回小于那部分大小 + 1

查找第 k 大:将 treap 按照 size 分成两部分,返回第二部分根节点

代码实现

  1 template<typename T,int maxsize>
  2 class Treap//FHQ Treap
  3 {
  4     public:
  5         Treap(){Seed=(int)(maxsize*999983ll%2147483647);clear();}
  6         void clear()
  7         {
  8             Total=Root=0;
  9             treap[0].Size=treap[0].Left=treap[0].Right=treap[0].Priority=0;
 10         }
 11         int create(T key)//initialize a node
 12         {
 13             int u=++Total;
 14             treap[u].Size=1;
 15             treap[u].Key=key;
 16             treap[u].Left=treap[u].Right=0;
 17             treap[u].Priority=Random();
 18             return u;
 19         }
 20         void KeySplit(int u,T key,int &x,int &y)//Split the treap by key
 21         {
 22             if(!u)
 23             {
 24                 x=y=0;
 25                 return ;
 26             }
 27             if(treap[u].Key<=key)
 28             {
 29                 x=u;
 30                 KeySplit(treap[u].Right,key,treap[u].Right,y);
 31             }
 32             else
 33             {
 34                 y=u;
 35                 KeySplit(treap[u].Left,key,x,treap[u].Left);
 36             }
 37             pushup(u);
 38         }
 39         void SizeSplit(int u,int Size,int &x,int &y)//Split the treap by size
 40         {
 41             if(!u)
 42             {
 43                 x=y=0;
 44                 return ;
 45             }
 46             if(treap[treap[u].Left].Size<Size)
 47             {
 48                 x=u;
 49                 SizeSplit(treap[u].Right,Size-treap[treap[u].Left].Size-1,treap[u].Right,y);
 50             }
 51             else
 52             {
 53                 y=u;
 54                 SizeSplit(treap[u].Left,Size,x,treap[u].Left);
 55             }
 56             pushup(u);
 57         }
 58         int merge(int x,int y)//guarantee that : all key in x < all key in y
 59         {
 60             if(!x||!y)
 61                 return x+y;
 62             if(treap[x].Priority>treap[y].Priority)
 63             {
 64                 treap[x].Right=merge(treap[x].Right,y);
 65                 pushup(x);
 66                 return x;
 67             }
 68             else
 69             {
 70                 treap[y].Left=merge(x,treap[y].Left);
 71                 pushup(y);
 72                 return y;
 73             }
 74         }
 75         void insert(T key)
 76         {
 77             int a,b;
 78             KeySplit(Root,key-1,a,b);
 79             Root=merge(merge(a,create(key)),b);
 80         }
 81         void remove(T key)
 82         {
 83             int a,b,c;
 84             KeySplit(Root,key,a,c);
 85             KeySplit(a,key-1,a,b);
 86             Root=merge(merge(a,merge(treap[b].Left,treap[b].Right)),c);
 87         }
 88         void removeAll(T key)
 89         {
 90             int a,b,c;
 91             KeySplit(Root,key,a,c);
 92             KeySplit(a,key-1,a,b);
 93             Root=merge(a,c);
 94         }
 95         int rank(T key)
 96         {
 97             int a,b,ret;
 98             KeySplit(Root,key-1,a,b);
 99             ret=treap[a].Size+1;
100             Root=merge(a,b);
101             return ret;
102         }
103         T at(int r)
104         {
105             int u=Root;
106             while(true)
107             {
108                 if(treap[treap[u].Left].Size+1==r)
109                     break;
110                 if(treap[treap[u].Left].Size+1<r)
111                 {
112                     r-=treap[treap[u].Left].Size+1;
113                     u=treap[u].Right;
114                 }
115                 else
116                 {
117                     u=treap[u].Left;
118                 }
119             }
120             return treap[u].Key;
121         }
122         T prev(T key)
123         {
124             int a,b;
125             T ret;
126             KeySplit(Root,key-1,a,b);
127             int u=a;
128             while(treap[u].Right)
129                 u=treap[u].Right;
130             ret=treap[u].Key;
131             Root=merge(a,b);
132             return ret;
133         }
134         T next(T key)
135         {
136             int a,b;
137             T ret;
138             KeySplit(Root,key,a,b);
139             int u=b;
140             while(treap[u].Left)
141                 u=treap[u].Left;
142             ret=treap[u].Key;
143             Root=merge(a,b);
144             return ret;
145         }
146         int size()
147         {
148             return treap[Root].Size;
149         }
150     private:
151         struct Node
152         {
153             T Key;
154             int Left,Right,Size,Priority;
155         }treap[maxsize];
156         int Seed,Total,Root;
157         int Random()
158         {
159             return Seed=(int)(Seed*943199ll%1000000007);
160         }
161         void pushup(int u)//update the info of a node
162         {
163             if(u)
164             {
165                 treap[u].Size=treap[treap[u].Left].Size+treap[treap[u].Right].Size+1;
166             }
167         }
168 };
View Code
posted @ 2021-09-20 21:05  Kevin090228  阅读(78)  评论(0)    收藏  举报