神奇的splay树

总结

  1. splay树是一种BST,其通过不断的splay操作维持树的平衡;其基本思想是将频率高的点(实际是每次查找的点)通过splay操作旋转到树根
  2. 核心操作:
  • update(x): 维护信息,类似线段树中的push_up

  • rotate(x): 单旋,即将x旋转到其父节点y的位置,需要注意顺序(替换y,x的子树加入y, y最为x的子树)

  • splay(int x,int s): 将x节点旋转到s下方。情况1:x,y,z共线,先rotate(y),再rotate(x); 情况2:不共线,rotate(x) 两次

  • find(int x): 找到后需旋转到根

  • insert(int x): 找到计数++,否则产生新节点并初始化

  • Next(int x,int f): 寻找前驱和后继

  • Delete(int x):先找前驱和后继,然后将前驱旋转到根节点root,后继旋转到root下面,然后删掉x(此时在t[nxt].ch[0])

  • kth(int x): 比较size即可

  • tarjan搞的算法都很难写,而且很容易写错呀,还不好调试吧

  • reference: blog1 blog2 blog3

模板题luogu3369

代码

  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. const int N=201000;
  4. struct splay_tree
  5. {
  6. int ff,cnt,ch[2],val,size;
  7. } t[N];
  8. int root,tot;//root==0 表示是空树 根节点的ff为0
  9. void update(int x)//更新节点x
  10. {
  11. t[x].size = t[t[x].ch[0]].size+t[t[x].ch[1]].size+t[x].cnt;
  12. }
  13. void rotate(int x)//对x进行单旋
  14. {
  15. int y = t[x].ff; int z =t[y].ff;
  16. int k = (t[y].ch[1]==x);
  17. t[z].ch[t[z].ch[1]==y] = x;// 用x替换z节点的儿子节点y
  18. t[x].ff = z;
  19. t[y].ch[k] = t[x].ch[1^k]; //先把 x的子树移到y
  20. t[t[x].ch[1^k]].ff = y;
  21. t[x].ch[1^k] = y; //y-x 与 x-y关系相反,构建x-y关系
  22. t[y].ff = x;
  23. update(y);update(x);// 先更新下面的层
  24. }
  25. void splay(int x,int s)//将x 旋转到 s下方, s==0 则是旋转到根
  26. {
  27. while(t[x].ff!=s)
  28. {
  29. int y=t[x].ff,z=t[y].ff;
  30. if (z!=s)//z==s 意味只需旋转一下x即可
  31. (t[z].ch[0]==y)^(t[y].ch[0]==x)?rotate(x):rotate(y);//如果 x,z,y同线,先旋转y,再旋转x;否则旋转两次x
  32. rotate(x);
  33. }
  34. if (s==0) //s==0 x旋转到根,更新root
  35. root=x;
  36. }
  37. void find(int x)
  38. {
  39. int u=root;
  40. if (!u)
  41. return ;//空树
  42. while(t[u].ch[x>t[u].val] && x!=t[u].val)//x>t[u].val 向右找, x< t[u].val 向左找
  43. u=t[u].ch[x>t[u].val];
  44. //也有可能找不到
  45. splay(u,0);//找到x,将其splay到根
  46. }
  47. void insert(int x) //插入操作
  48. {
  49. int u=root,ff=0;
  50. while(u && t[u].val!=x)
  51. {
  52. ff=u;
  53. u=t[u].ch[x>t[u].val];
  54. }
  55. if (u)//找到元素x的节点,计数++
  56. t[u].cnt++;
  57. else//没有找到则产生新节点
  58. {
  59. u=++tot;
  60. if (ff)//ff!=0 u不是根节点
  61. t[ff].ch[x>t[ff].val]=u;
  62. t[u].ch[0]=t[u].ch[1]=0;//初始化t[u]
  63. t[tot].ff=ff;
  64. t[tot].val=x;
  65. t[tot].cnt=1;
  66. t[tot].size=1;
  67. }
  68. splay(u,0);//u splay到根节点
  69. }
  70. int Next(int x,int f)//f=0 表示前驱 f=1表示后继
  71. {
  72. find(x);// 如果找到x所在节点 会被splay到根节点
  73. int u=root;
  74. if (t[u].val>x && f) //find没有找到x
  75. return u;
  76. if (t[u].val<x && !f)
  77. return u;
  78. //find 找打了x,且此时再根节点上
  79. u=t[u].ch[f];
  80. while(t[u].ch[f^1])//左子树的最右边节点/右子树的最左边节点
  81. u=t[u].ch[f^1];
  82. return u;
  83. }
  84. void Delete(int x)
  85. {
  86. int last=Next(x,0);
  87. int Net=Next(x,1);
  88. splay(last,0);
  89. splay(Net,last); //找到前驱和后继并将前驱splay到根节点,后继splay到根节点下面; 则x代表的节点在是根节点的左儿子
  90. int del=t[Net].ch[0];
  91. if (t[del].cnt>1)//计数--
  92. {
  93. t[del].cnt--;
  94. splay(del,0);
  95. }
  96. else
  97. t[Net].ch[0]=0;//彻底删掉
  98. }
  99. int kth(int x)
  100. {
  101. int u=root;
  102. while(t[u].size<x)//不存在排名为x
  103. return 0;
  104. while(1)
  105. {
  106. int y=t[u].ch[0];
  107. if (x>t[y].size+t[u].cnt) //在右子树
  108. {
  109. x-=t[y].size+t[u].cnt;
  110. u=t[u].ch[1];
  111. }
  112. else if (t[y].size>=x)//在左子树
  113. u=y;
  114. else //就在u
  115. return t[u].val;
  116. }
  117. }
  118. /*
  119. 插入数值x。
  120. 删除数值x(若有多个相同的数,应只删除一个)。
  121. 查询数值x的排名(若有多个相同的数,应输出最小的排名)。
  122. 查询排名为x的数值。
  123. 求数值x的前驱(前驱定义为小于x的最大的数)。
  124. 求数值x的后继(后继定义为大于x的最小的数)。
  125. */
  126. int main()
  127. {
  128. int n;
  129. scanf("%d",&n);
  130. insert(1e9); //始终保持能够找到前驱和后继
  131. insert(-1e9);
  132. while(n--)
  133. {
  134. int opt,x;
  135. scanf("%d%d",&opt,&x);
  136. if (opt==1)
  137. insert(x);
  138. if (opt==2)
  139. Delete(x);
  140. if (opt==3)
  141. {
  142. find(x);
  143. printf("%d\n",t[t[root].ch[0]].size);
  144. }
  145. if (opt==4)
  146. printf("%d\n",kth(x+1));
  147. if (opt==5)
  148. printf("%d\n",t[Next(x,0)].val);
  149. if (opt==6)
  150. printf("%d\n",t[Next(x,1)].val);
  151. }
  152. return 0;
  153. }

神奇的splay树的更多相关文章

  1. Splay树-Codevs 1296 营业额统计

    Codevs 1296 营业额统计 题目描述 Description Tiger最近被公司升任为营业部经理,他上任后接受公司交给的第一项任务便是统计并分析公司成立以来的营业情况. Tiger拿出了公司 ...

  2. ZOJ3765 Lights Splay树

    非常裸的一棵Splay树,需要询问的是区间gcd,但是区间上每个数分成了两种状态,做的时候分别存在val[2]的数组里就好.区间gcd的时候基本上不支持区间的操作了吧..不然你一个区间里加一个数gcd ...

  3. Splay树再学习

    队友最近可能在学Splay,然后让我敲下HDU1754的题,其实是很裸的一个线段树,不过用下Splay也无妨,他说他双旋超时,单旋过了,所以我就敲来看下.但是之前写的那个Splay越发的觉得不能看,所 ...

  4. 暑假学习日记:Splay树

    从昨天开始我就想学这个伸展树了,今天花了一个上午2个多小时加下午2个多小时,学习了一下伸展树(Splay树),学习的时候主要是看别人博客啦~发现下面这个博客挺不错的http://zakir.is-pr ...

  5. 1439. Battle with You-Know-Who(splay树)

    1439 路漫漫其修远兮~ 手抄一枚splay树 长长的模版.. 关于spaly树的讲解   网上很多随手贴一篇 貌似这题可以用什么bst啦 堆啦 平衡树啦 等等 这些本质都是有共同点的 查找.删除特 ...

  6. 伸展树(Splay树)的简要操作

    伸展树(splay树),是二叉排序树的一种.[两个月之前写过,今天突然想写个博客...] 伸展树和一般的二叉排序树不同的是,在每次执行完插入.查询.删除等操作后,都会自动平衡这棵树.(说是自动,也就是 ...

  7. [Splay伸展树]splay树入门级教程

    首先声明,本教程的对象是完全没有接触过splay的OIer,大牛请右上角.. 首先引入一下splay的概念,他的中文名是伸展树,意思差不多就是可以随意翻转的二叉树 PS:百度百科中伸展树读作:BoGa ...

  8. hdu 3436 splay树+离散化*

    Queue-jumpers Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) To ...

  9. hdu 1890 splay树

    Robotic Sort Time Limit: 6000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Tot ...

随机推荐

  1. Spring MVC 一次简单的 CRUD

    基本环境搭建 1.数据库 和 实体类 的名字相同,实体类 属性名即 数据库 字段名. 2.创建 实体类 对应 dao 类,持久层框架 mybatis 正处于学习中,这里就用原始的 jdbc 操作了. ...

  2. Spring MVC-控制器(Controller)-属性方法名称解析器(Properties Method Name Resolver )示例(转载实践)

    以下内容翻译自:https://www.tutorialspoint.com/springmvc/springmvc_propertiesmethodnameresolver.htm 说明:示例基于S ...

  3. Linux环境变量设置命令export(转)

    Linux export命令用于设置或显示环境变量. 在shell中执行程序时,shell会提供一组环境变量.export可新增,修改或删除环境变量,供后续执行的程序使用.export的效力仅及于该次 ...

  4. Android shape自定义形状,设置渐变色

      <?xml version="1.0" encoding="utf-8"?> <!-- android:shape=["rect ...

  5. Android系统升级那些事儿【转】

    本文转载自:http://blog.csdn.net/chenyufei1013/article/details/12705719 版权声明:本文为博主原创文章,未经博主允许不得转载.   目录(?) ...

  6. DockPanelSuite中的DocumentStyle

    首先明确一个概念Mdi的完整词组:Multiple document interface namespace WeifenLuo.WinFormsUI.Docking { public enum Do ...

  7. Ubuntu17.10 下配置caffe 仅CPU i386可以直接apt install caffe-cpu,但是怎么运行mnist代码我懵逼了

    Ubuntu16.04下配置caffe(仅CPU)  参考:http://blog.csdn.net/zt_1995/article/details/56283249   第二次配置caffe环境,依 ...

  8. Coursera Algorithms week1 算法分析 练习测验: Egg drop 扔鸡蛋问题

    题目原文: Suppose that you have an n-story building (with floors 1 through n) and plenty of eggs. An egg ...

  9. 原生JS---2

    js中的程序控制语句 常见的程序有三种执行结构: 1. 顺序结构 2. 分支结构 3. 循环结构 顺序结构:程序从第一行开始执行,按顺序执行到最后一行 分支结构:就像一条岔路口,必须选择且只能选择其中 ...

  10. 消除svn选定(checkout)桌面上文件显示一大堆问号。

    图片: 解决方法一: 桌面右键选择TortoiseSVN——>点击Settings,如下图,选中Icon Overlays(图标覆盖),去勾选Fixed drives(本地磁盘),点击确定,按F ...