平衡树的板题,用Treap实现。

具体参见注释,写的很详细了,包括了原理,实现以及注意事项

蒟蒻写个注释板子写了两天,太弱了QAQ

感谢niiick指导

Code

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<queue>
  4. #include<cstring>
  5. #include<algorithm>
  6. #include<climits>
  7. typedef long long LL;
  8. using namespace std;
  9. int RD(){
  10. int out = 0,flag = 1;char c = getchar();
  11. while(c < '0' || c >'9'){if(c == '-')flag = -1;c = getchar();}
  12. while(c >= '0' && c <= '9'){out = out * 10 + c - '0';c = getchar();}
  13. return flag * out;
  14. }
  15. //第一次打treap,不压行写注释XD
  16. const int maxn = 1000019,INF = 1e9;
  17. //平衡树,利用BST性质查询和修改,利用随机和堆优先级来保持平衡,把树的深度控制在log N,保证了操作效率
  18. //基本平衡树有以下几个比较重要的函数:新建,插入,删除,旋转
  19. //节点的基本属性有val(值),dat(随机出来的优先级)
  20. //通过增加属性,结合BST的性质可以达到一些效果,如size(子树大小,查询排名),cnt(每个节点包含的副本数)等
  21. int na;
  22. int ch[maxn][2];//[i][0]代表i左儿子,[i][1]代表i右儿子
  23. int val[maxn],dat[maxn];
  24. int size[maxn],cnt[maxn];
  25. int tot,root;
  26. int New(int v){//新增节点,
  27. val[++tot] = v;//节点赋值
  28. dat[tot] = rand();//随机优先级
  29. size[tot] = 1;//目前是新建叶子节点,所以子树大小为1
  30. cnt[tot] = 1;//新建节点同理副本数为1
  31. return tot;
  32. }
  33. void pushup(int id){//和线段树的pushup更新一样
  34. size[id] = size[ch[id][0]] + size[ch[id][1]] + cnt[id];//本节点子树大小 = 左儿子子树大小 + 右儿子子树大小 + 本节点副本数
  35. }
  36. void build(){
  37. root = New(-INF),ch[root][1] = New(INF);//先加入正无穷和负无穷,便于之后操作(貌似不加也行)
  38. pushup(root);//因为INF > -INF,所以是右子树,
  39. }
  40. void Rotate(int &id,int d){//id是引用传递,d(irection)为旋转方向,0为左旋,1为右旋
  41. int temp = ch[id][d ^ 1];//旋转理解:找个动图看一看就好(或参见其他OIer的blog)
  42. ch[id][d ^ 1] = ch[temp][d];//这里讲一个记忆技巧,这些数据都是被记录后马上修改
  43. ch[temp][d] = id;//所以像“Z”一样
  44. id = temp;//比如这个id,在上一行才被记录过,ch[temp][d]、ch[id][d ^ 1]也是一样的
  45. pushup(ch[id][d]),pushup(id);//旋转以后size会改变,看图就会发现只更新自己和转上来的点,pushup一下,注意先子节点再父节点
  46. }//旋转实质是({在满足BST的性质的基础上比较优先级}通过交换本节点和其某个叶子节点)把链叉开成二叉形状(从而控制深度),可以看图理解一下
  47. void insert(int &id,int v){//id依然是引用,在新建节点时可以体现
  48. if(!id){
  49. id = New(v);//若节点为空,则新建一个节点
  50. return ;
  51. }
  52. if(v == val[id])cnt[id]++;//若节点已存在,则副本数++;
  53. else{//要满足BST性质,小于插到左边,大于插到右边
  54. int d = v < val[id] ? 0 : 1;//这个d是方向的意思,按照BST的性质,小于本节点则向左,大于向右
  55. insert(ch[id][d],v);//递归实现
  56. if(dat[id] < dat[ch[id][d]])Rotate(id,d ^ 1);//(参考一下图)与左节点交换右旋,与右节点交换左旋
  57. }
  58. pushup(id);//现在更新一下本节点的信息
  59. }
  60. void Remove(int &id,int v){//最难de部分了
  61. if(!id)return ;//到这了发现查不到这个节点,该点不存在,直接返回
  62. if(v == val[id]){//检索到了这个值
  63. if(cnt[id] > 1){cnt[id]--,pushup(id);return ;}//若副本不止一个,减去一个就好
  64. if(ch[id][0] || ch[id][1]){//发现只有一个值,且有儿子节点,我们只能把值旋转到底部删除
  65. if(!ch[id][1] || dat[ch[id][0]] > dat[ch[id][1]]){//当前点被移走之后,会有一个新的点补上来(左儿子或右儿子),按照优先级,优先级大的补上来
  66. Rotate(id,1),Remove(ch[id][1],v);//我们会发现,右旋是与左儿子交换,当前点变成右节点;左旋则是与右儿子交换,当前点变为左节点
  67. }
  68. else Rotate(id,0),Remove(ch[id][0],v);
  69. pushup(id);
  70. }
  71. else id = 0;//发现本节点是叶子节点,直接删除
  72. return ;//这个return对应的是检索到值de所有情况
  73. }
  74. v < val[id] ? Remove(ch[id][0],v) : Remove(ch[id][1],v);//继续BST性质
  75. pushup(id);
  76. }
  77. int get_rank(int id,int v){
  78. if(!id)return 0;//若查询值不存在,返回
  79. if(v == val[id])return size[ch[id][0]] + 1;//查询到该值,由BST性质可知:该点左边值都比该点的值(查询值)小,故rank为左儿子大小 + 1
  80. else if(v < val[id])return get_rank(ch[id][0],v);//发现需查询的点在该点左边,往左边递归查询
  81. else return size[ch[id][0]] + cnt[id] + get_rank(ch[id][1],v);//若查询值大于该点值。说明询问点在当前点的右侧,且此点的值都小于查询值,所以要加上cnt[id]
  82. }
  83. int get_val(int id,int rank){
  84. if(!id)return INF;//一直向右找找不到,说明是正无穷
  85. if(rank <= size[ch[id][0]])return get_val(ch[id][0],rank);//左边排名已经大于rank了,说明rank对应的值在左儿子那里
  86. else if(rank <= size[ch[id][0]] + cnt[id])return val[id];//上一步排除了在左区间的情况,若是rank在左与中(目前节点)中,则直接返回目前节点(中区间)的值
  87. else return get_val(ch[id][1],rank - size[ch[id][0]] - cnt[id]);//剩下只能在右区间找了,rank减去左区间大小和中区间,继续递归
  88. }
  89. int get_pre(int v){
  90. int id = root,pre;//递归不好返回,以循环求解
  91. while(id){//查到节点不存在为止
  92. if(val[id] < v)pre = val[id],id = ch[id][1];//满足当前节点比目标小,往当前节点的右侧寻找最优值
  93. else id = ch[id][0];//无论是比目标节点大还是等于目标节点,都不满足前驱条件,应往更小处靠近
  94. }
  95. return pre;
  96. }
  97. int get_next(int v){
  98. int id = root,next;
  99. while(id){
  100. if(val[id] > v)next = val[id],id = ch[id][0];//同理,满足条件向左寻找更小解(也就是最优解)
  101. else id = ch[id][1];//与上方同理
  102. }
  103. return next;
  104. }
  105. int main(){
  106. build();//不要忘记初始化[运行build()会连同root一并初始化,所以很重要]
  107. na = RD();
  108. for(int i = 1;i <= na;i++){
  109. int cmd = RD(),x = RD();
  110. if(cmd == 1)insert(root,x);//函数都写好了,注意:需要递归的函数都从根开始,不需要递归的函数直接查询
  111. else if(cmd == 2)Remove(root,x);
  112. else if(cmd == 3)printf("%d\n",get_rank(root,x) - 1);//注意:因为初始化时插入了INF和-INF,所以查询排名时要减1(-INF不是第一小,是“第零小”)
  113. else if(cmd == 4)printf("%d\n",get_val(root,x + 1));//同理,用排名查询值得时候要查x + 1名,因为第一名(其实不是)是-INF
  114. else if(cmd == 5)printf("%d\n",get_pre(x));
  115. else if(cmd == 6)printf("%d\n",get_next(x));
  116. }
  117. return 0;
  118. }

平衡树【Treap】的更多相关文章

  1. hiho #1325 : 平衡树·Treap

    #1325 : 平衡树·Treap 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 小Ho:小Hi,我发现我们以前讲过的两个数据结构特别相似. 小Hi:你说的是哪两个啊? ...

  2. hiho一下103周 平衡树·Treap

    平衡树·Treap 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 小Ho:小Hi,我发现我们以前讲过的两个数据结构特别相似. 小Hi:你说的是哪两个啊? 小Ho:就是二 ...

  3. 算法模板——平衡树Treap 2

    实现功能:同平衡树Treap 1(BZOJ3224 / tyvj1728) 这次的模板有了不少的改进,显然更加美观了,几乎每个部分都有了不少简化,尤其是删除部分,这个参照了hzwer神犇的写法,在此鸣 ...

  4. 【山东省选2008】郁闷的小J 平衡树Treap

    小J是国家图书馆的一位图书管理员,他的工作是管理一个巨大的书架.虽然他很能吃苦耐劳,但是由于这个书架十分巨大,所以他的工作效率总是很低,以致他面临着被解雇的危险,这也正是他所郁闷的.具体说来,书架由N ...

  5. Hihocoder 1325 平衡树·Treap(平衡树,Treap)

    Hihocoder 1325 平衡树·Treap(平衡树,Treap) Description 小Ho:小Hi,我发现我们以前讲过的两个数据结构特别相似. 小Hi:你说的是哪两个啊? 小Ho:就是二叉 ...

  6. HihoCoder 1325 平衡树·Treap

    HihoCoder 1325 平衡树·Treap 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 小Ho:小Hi,我发现我们以前讲过的两个数据结构特别相似. 小Hi:你说 ...

  7. 普通平衡树Treap(含旋转)学习笔记

    浅谈普通平衡树Treap 平衡树,Treap=Tree+heap这是一个很形象的东西 我们要维护一棵树,它满足堆的性质和二叉查找树的性质(BST),这样的二叉树我们叫做平衡树 并且平衡树它的结构是接近 ...

  8. HihoCoder1325 : 平衡树·Treap(附STL版本)

    平衡树·Treap 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 小Ho:小Hi,我发现我们以前讲过的两个数据结构特别相似. 小Hi:你说的是哪两个啊? 小Ho:就是二 ...

  9. luoguP3369[模板]普通平衡树(Treap/SBT) 题解

    链接一下题目:luoguP3369[模板]普通平衡树(Treap/SBT) 平衡树解析 #include<iostream> #include<cstdlib> #includ ...

  10. 2021.12.06 平衡树——Treap

    2021.12.06 平衡树--Treap https://www.luogu.com.cn/blog/HOJQVFNA/qian-xi-treap-ping-heng-shu 1.二叉搜索树 1.1 ...

随机推荐

  1. New begin

    Purpose 今天更换了id,希望重新沉淀. 晚上看到国外一个博客,落款有个中文: 敬惜字纸. 共勉.

  2. 一个小时搭建一个全栈 Web 应用框架

    把想法变为现实的能力是空想家与实干家的区别.不管你是在一家跨国公司工作,还是正在为自己的创业公司而努力,那些有能力将创意转化为真正产品的人,都具有宝贵的技能并拥有明显的实力.如果你能在不到一个小时的时 ...

  3. 使用Node.js 搭建http服务器 http-server 模块

    1. 安装 http-server 模块 npm install http-server -g   全局安装 2.在需要的文件夹   启动 http-server  默认的端口是8080    可以使 ...

  4. Scrum立会报告+燃尽图(十月二十八日总第十九次)

    此作业要求参见:https://edu.cnblogs.com/campus/nenu/2018fall/homework/2288 项目地址:https://git.coding.net/zhang ...

  5. vue+vue-video-player实现弹窗播放视频

    将视频播放器标签放在对话框标签中,实现弹窗 template 中 <el-dialog :visible.sync="dialogVisible" width='680px' ...

  6. Eclipse添加Jquery和javascript的智能提示

    使用Eclipse写Jquery和Javascript代码的时候,是没有智能提示的.我们可以使用一个插件来解决这个问题. 安装完成后,Eclipse会自动重启.重启之后,我们在项目上右键,   根据自 ...

  7. lintcode-491-回文数

    491-回文数 判断一个正整数是不是回文数. 回文数的定义是,将这个数反转之后,得到的数仍然是同一个数. 注意事项 给的数一定保证是32位正整数,但是反转之后的数就未必了. 样例 11, 121, 1 ...

  8. lintcode-424-逆波兰表达式求值

    424-逆波兰表达式求值 求逆波兰表达式的值. 在逆波兰表达法中,其有效的运算符号包括 +, -, *, / .每个运算对象可以是整数,也可以是另一个逆波兰计数表达. 样例 ["2" ...

  9. C语言文法推导

  10. Objective - C 之协议

    一.创建方法: 二.实现过程: 1.遵循协议: @protocol NurseWorkingProtocol <NSObject>   //<> 表示遵守协议,创建时就有(Nu ...