[关闭]
@Aonrbet 2020-07-08T09:48:11.000000Z 字数 3425 阅读 477

平衡树学习笔记

Study


FHQ Treap

前置芝士

BST的性质:
根节点左子树的值均小于等于根节点,右子树的值均大于根节点 

例题

我们需要支持以下操作

split

  1. inline void split(int now, int &x, int &y, int k){ //裂开
  2. if(!now) return (void)(x = y = 0); //如果没有根即没有树可裂,x,y均为0
  3. if(val[now] <= k){
  4. x = now; //如果根的值小于等于k则k要大于now左子树所有的值,所以把左子树裂开,让右子树接着裂
  5. split(ch[now][1], ch[x][1], y, k);
  6. }else{
  7. y = now; //相反
  8. split(ch[now][0], x, ch[y][0], k);
  9. }
  10. up(now);
  11. //裂开以后所有比k小的值均在x树上,比k大的值均在y树上
  12. }

merge

  1. inline void merge(int &now, int x, int y){
  2. if(!siz[x] or !siz[y]) return (void)(now = siz[x] ? x : y); //x 和 y 有一个空树的话直接返回不空树
  3. if(key[x] < key[y]){ //当x的键值小于y的键值 ,让y和x的右子树并
  4. now = x;
  5. merge(ch[now][1], ch[x][1], y);
  6. up(now);
  7. }else{
  8. now = y;//相反
  9. merge(ch[now][0], x, ch[y][0]);
  10. up(now);
  11. }
  12. }

delete

如何删除一个数num,很巧妙的方法

1.我们把root树以num为关键字裂开,此时x树上的值均小于等于num

2.我们再去裂x树,此时以num-1为关键字,则裂开的两个树中有一个树上的值均等于 num(我们设这颗树为z)

3.直接合并z的左右子树,此时已经把z的根节点移除

4.合并

  1. inline void del(int num){
  2. split(root, x, y, num);
  3. split(x, x, z, num-1);
  4. merge(z, ch[z][0], ch[z][1]);
  5. merge(x, x, z);
  6. merge(root, x, y);
  7. }

insert

很简单

  1. inline void ins(int num){
  2. split(root, x, y, num);
  3. merge(x, x, new_node(num));
  4. merge(root, x, y);
  5. }

kth

寻找第k排名的数

(暂时还不透彻,会补上注释的....汗)

  1. inline int kth(int now, int k){
  2. while(1){
  3. if(k <= siz[ch[now][0]]){
  4. now = ch[now][0];
  5. }else{
  6. if(k == siz[ch[now][0]] + 1) return now;
  7. else{
  8. k -= siz[ch[now][0]] + 1;
  9. now = ch[now][1];
  10. }
  11. }
  12. }
  13. }

rnk

num的排名就是所有小于num的数的数量+1,此时已经显然

  1. inline int rnk(int num){
  2. split(root, x, y, num-1);
  3. int res = siz[x] + 1;
  4. merge(root, x, y);
  5. return res;
  6. }

pre

前驱怎么找?

前驱是不是所有小于num的值里最大的值!

我们以num-1为关键字去把root树裂开,此时x树上的值是所有小于num的数,

我们再找x树里面排名为size[x]的数,是不是就是所有小于num的数里面最大的数?即前驱。

  1. inline int pre(int num){ //找前驱
  2. split(root, x, y, num-1);
  3. int res = val[kth(x,siz[x])];
  4. merge(root, x, y);
  5. return res;
  6. }

nxt

后继怎么找...

后继是不是所有大于num的值里最小的值.....

我们以num为关键字去把root树裂开,此时y树上的值是所有大于num的数,

我们再找y树里面排名为1的数,是不是就是所有大于num的数里面最小的数?即后继。

  1. inline int nxt(int num){
  2. split(root, x, y, num);
  3. int res = val[kth(y,1)];
  4. merge(root, x, y);
  5. return res;
  6. }

总代码看不看无所谓的吧,反正就是并在一起了....

code

  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. const int maxn = 1e5 + 10;
  4. struct Treap{
  5. int ch[maxn][2], val[maxn], key[maxn], siz[maxn];
  6. int sum, root, x, y, z, n; //sum记录节点编号
  7. inline void up(int now){ //维护一下大小
  8. siz[now] = siz[ch[now][0]] + siz[ch[now][1]] + 1; //它的大小就等于左右儿子大小的和+自己这个点
  9. }
  10. inline int new_node(int num){ //新建一个点
  11. siz[++sum] = 1; //新建一个树(点),但大小为1
  12. val[sum] = num; //值为num
  13. key[sum] = rand(); //随机
  14. return sum; //返回节点编号
  15. }
  16. inline void split(int now, int &x, int &y, int k){
  17. if(!now) return (void)(x = y = 0);
  18. if(val[now] <= k){
  19. x = now;
  20. split(ch[now][1], ch[x][1], y, k);
  21. }else{
  22. y = now;
  23. split(ch[now][0], x, ch[y][0], k);
  24. }
  25. up(now);
  26. }
  27. inline void merge(int &now, int x, int y){
  28. if(!siz[x] or !siz[y]) return (void)(now = siz[x] ? x : y);
  29. if(key[x] < key[y]){
  30. now = x;
  31. merge(ch[now][1], ch[x][1], y);
  32. up(now);
  33. }else{
  34. now = y;
  35. merge(ch[now][0], x, ch[y][0]);
  36. up(now);
  37. }
  38. }
  39. inline void del(int num){
  40. split(root, x, y, num);
  41. split(x, x, z, num-1);
  42. merge(z, ch[z][0], ch[z][1]);
  43. merge(x, x, z);
  44. merge(root, x, y);
  45. }
  46. inline void ins(int num){
  47. split(root, x, y, num);
  48. merge(x, x, new_node(num));
  49. merge(root, x, y);
  50. }
  51. inline int kth(int now, int k){
  52. while(1){
  53. if(k <= siz[ch[now][0]]){
  54. now = ch[now][0];
  55. }else{
  56. if(k == siz[ch[now][0]] + 1) return now;
  57. else{
  58. k -= siz[ch[now][0]] + 1;
  59. now = ch[now][1];
  60. }
  61. }
  62. }
  63. }
  64. inline int rnk(int num){
  65. split(root, x, y, num-1);
  66. int res = siz[x] + 1;
  67. merge(root, x, y);
  68. return res;
  69. }
  70. inline int pre(int num){
  71. split(root, x, y, num-1);
  72. int res = val[kth(x,siz[x])];
  73. merge(root, x, y);
  74. return res;
  75. }
  76. inline int nxt(int num){
  77. split(root, x, y, num);
  78. int res = val[kth(y,1)];
  79. merge(root, x, y);
  80. return res;
  81. }
  82. void main(){
  83. root = 0, x = y = z = 0;
  84. scanf("%d", &n);
  85. for(int i = 1, q, w; i <= n; i ++){
  86. scanf("%d%d", &q, &w);
  87. if(q == 1) ins(w);
  88. if(q == 2) del(w);
  89. if(q == 3) printf("%d\n", rnk(w));
  90. if(q == 4) printf("%d\n", val[kth(root,w)]);
  91. if(q == 5) printf("%d\n", pre(w));
  92. if(q == 6) printf("%d\n", nxt(w));
  93. }
  94. }
  95. }FHQ;
  96. signed main(){
  97. FHQ.main();
  98. return 0;
  99. }

Splay

wanqua

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注