什么时候左旋右旋

Treap如何写 - 图1

当对树的形态进行操作的时候 才会进行zig/zag 各种查询操作均用不上

  1. 插入的时候判断一下新增的节点的方向上 是否需要maintain
  2. 删除的时候通过旋转 把要删除的节点旋转至 叶子 然后删除
  3. 右旋先对右儿子进行pushup 再对父亲做
  4. 新增节点 删除节点 以及左旋右旋后 pushup

常见issues

见代码注释

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<algorithm>
  4. #include<cstring>
  5. #include<cmath>
  6. #include<ctime>
  7. using namespace std;
  8. // #define int long long
  9. const int maxn = 1e6 + 10;
  10. const int INF = 0x7fffffff;
  11. struct node{
  12. int l, r;
  13. int val, dat;
  14. int cnt, siz;
  15. }treap[maxn << 2];
  16. int tot, root , n;
  17. int New(int val){
  18. treap[++tot].val = val;
  19. treap[tot].dat = rand();
  20. treap[tot].cnt = treap[tot].siz = 1;
  21. return tot;
  22. }
  23. void pushup(int x){
  24. treap[x].siz = treap[treap[x].l].siz + treap[treap[x].r].siz + treap[x].cnt;
  25. }
  26. void build(){
  27. /*
  28. 在找前驱和后继的时候防止找到空节点
  29. */
  30. New(-INF);
  31. New(INF);
  32. root = 1;
  33. treap[1].r = 2;
  34. pushup(root);
  35. }
  36. /*
  37. 右旋, 本来的父亲节点成为了自己左儿子的右儿子
  38. */
  39. void zig(int &x){
  40. int y = treap[x].l;
  41. treap[x].l = treap[y].r; //原本父亲节点的左儿子变成了 他左儿子的右儿子
  42. treap[y].r = x; // 左儿子的右儿子 变成了原本的父亲节点
  43. x = y;
  44. pushup(treap[x].r);
  45. pushup(x);
  46. }
  47. /*
  48. 左旋, 本来的父亲节点成为了自己右儿子的左儿子
  49. */
  50. void zag(int &x){
  51. int y = treap[x].r;
  52. treap[x].r = treap[y].l;
  53. treap[y].l = x;
  54. x = y;
  55. pushup(treap[x].l);
  56. pushup(x);
  57. }
  58. void insert(int &x, int val){
  59. if(x == 0){
  60. x = New(val);
  61. return;
  62. }
  63. if(val == treap[x].val){ //已经有的值增加计数即可
  64. treap[x].cnt++;
  65. pushup(x);
  66. return;
  67. }
  68. if(val < treap[x].val){
  69. insert(treap[x].l, val);
  70. if(treap[x].dat < treap[treap[x].l].dat){
  71. zig(x);
  72. }
  73. } else{
  74. insert(treap[x].r, val);
  75. if(treap[x].dat < treap[treap[x].r].dat){
  76. zag(x);
  77. }
  78. }
  79. pushup(x);
  80. }
  81. int getPre(int val){
  82. int ans = 1;
  83. int x = root;
  84. while(x){
  85. if(val == treap[x].val){
  86. if(treap[x].l > 0){
  87. x = treap[x].l;
  88. while(treap[x].r > 0) x = treap[x].r; //往左子树中的右边一直找
  89. ans = x;
  90. }
  91. break;
  92. }
  93. if(treap[x].val < val&&treap[x].val > treap[ans].val) ans = x;
  94. //可以作为前驱的答案 比当前答案更优
  95. x = val < treap[x].val? treap[x].l : treap[x].r;
  96. }
  97. return treap[ans].val;
  98. }
  99. int getNxt(int val){
  100. int ans = 2;
  101. int x = root;
  102. while(x){
  103. if(val == treap[x].val){
  104. if(treap[x].r > 0){
  105. x = treap[x].r;
  106. while(treap[x].l > 0) x = treap[x].l;
  107. ans = x;
  108. }
  109. break;
  110. }
  111. if(treap[x].val > val&&treap[x].val < treap[ans].val) ans = x;
  112. x = val < treap[x].val? treap[x].l:treap[x].r;
  113. }
  114. return treap[ans].val;
  115. }
  116. void remove(int &x, int val){
  117. if(x == 0) return;
  118. if(val == treap[x].val){
  119. if(treap[x].cnt > 1){
  120. treap[x].cnt--;
  121. pushup(x);
  122. return;
  123. }
  124. if(treap[x].l || treap[x].r){
  125. if(treap[x].r == 0|| treap[treap[x].l].dat > treap[treap[x].r].dat){
  126. zig(x);
  127. remove(treap[x].r, val);
  128. } else{
  129. zag(x);
  130. remove(treap[x].l, val);
  131. }
  132. pushup(x);
  133. } else{
  134. x = 0;
  135. }
  136. return;
  137. }
  138. val < treap[x].val? remove(treap[x].l, val):remove(treap[x].r, val);
  139. pushup(x);
  140. }
  141. int getRankByVal(int x, int val){
  142. if(x == 0) return 0;
  143. if(val == treap[x].val){
  144. return treap[treap[x].l].siz + 1; // 比他小的数+1 即得到排名
  145. }
  146. if(val < treap[x].val){
  147. return getRankByVal(treap[x].l, val); // 比当前值还要小 继续往左子树找
  148. } else{
  149. /*
  150. 左边的小的 全部算上 当前节点也比你要找的值小 有多少副本数全部算上
  151. */
  152. return treap[treap[x].l].siz + getRankByVal(treap[x].r, val) + treap[x].cnt;
  153. }
  154. }
  155. int getValByRank(int x, int rank){
  156. if(x == 0) return INF;
  157. if(treap[treap[x].l].siz >= rank){
  158. //比你小的数 还有很多 还要继续往左边找
  159. return getValByRank(treap[x].l, rank);
  160. } else if((treap[treap[x].l].siz + treap[x].cnt) >= rank){
  161. //本身的副本数刚好覆盖掉了 就是当前的值
  162. return treap[x].val;
  163. } else{
  164. //继续往比当前值大的区域寻找 要找的名词减去当前点以及它左子树的贡献
  165. return getValByRank(treap[x].r, rank - treap[treap[x].l].siz - treap[x].cnt);
  166. }
  167. }
  168. int main()
  169. {
  170. // freopen("out.txt", "w", stdout);
  171. build();
  172. int op, x;
  173. scanf("%d", &n);
  174. for(int i = 0; i < n; ++i)
  175. {
  176. scanf("%d%d", &op, &x);
  177. switch(op)
  178. {
  179. case 1: //插入
  180. insert(root, x);
  181. break;
  182. case 2: //删除
  183. remove(root, x);
  184. break;
  185. case 3: //按值查询 排名
  186. printf("%d\n", getRankByVal(root, x) - 1);
  187. break;
  188. case 4: //按排名查询值
  189. printf("%d\n", getValByRank(root, x + 1));
  190. break;
  191. case 5: //前驱
  192. printf("%d\n", getPre(x));
  193. break;
  194. case 6: //后继
  195. printf("%d\n", getNxt(x));
  196. break;
  197. }
  198. }
  199. return 0;
  200. }