• 前言
  • splay出现的背景
  • 它的操作
    • push_up
    • rotate
    • splay
    • find
    • insert
    • next
    • delete
    • findkth
    • main

前言

暑假快过完了,大家感觉是不是很棒!gay房也体验过最新的断电模拟器了.
好吧,在开讲之前我还是得说一个.刀剑神域的外传实在太好看了!
除了精彩的战斗场面,神一般的人设和强大的剧情,比起前作来看又多了一样我最喜欢的东西.
神曲!我从一名听众到神崎艾莎的粉丝只经过了一首歌.
Independence.燃到甚至有点伤感.看过作品的人应该能懂歌曲中隐含的感情.
我是一边听这首歌一边写的这个博客,大家也可以一边听歌一边欢乐地享受这一讲的内容.
好了讲正事.终于要开始研究神奇的数据结构了.
至于讲哪个,题目已经说了,我也就不扯淡了.
其实splay还是非常好懂的.
至于线段树什么的我自己也是先贺了一遍代码然后写一遍博客,只要3个月不学自会.
现在离AFO也差不多3个月,大概是能够会的.

splay出现的背景

大家都知道二叉查找树有概率会退化成链,这样时间复杂度就会大大增高.
这个时候有个叫Robert Tarjan 的男人和另一个叫Daniel Sleator的人发明了这样的一种数据结构.
它在每次询问的时候可以将树旋转(也就是splay这个操作)来维护树的平衡性.

它的操作

我们就以模板[普通平衡树]为例来学习这种数据结构.
那么这种数据结构应该要支持插入删除,求第 k k k大,求排名,求前驱和后继.
首先我们定义struct splaytree.

const int yuzu=1e5;
typedef int fuko[yuzu|10];
/*事先准备maxsize和大小为maxsize的数组.*/
struct splaytree{
fuko ch[2],sz,cnt,fa,val;
/*ch是孩子,标号0,1分别表示左孩子和右孩子;sz是这个节点的子树大小(包括自己);cnt是这个节点保存的相同的值的个数(万一出现同样的数字只要cnt[u]++就可以了);fa是节点的父亲;val是节点保存的值.按照本题的要求来讲开这一些数组就够了.*/
然而我还要define一些东西.
#define ls(x) ch[0][x]
#define rs(x) ch[1][x]
#define ws(x,y) (rs(x)==y)
/*这个是which_son的简称,就是看y是x的左孩子还是右孩子.如果x的右孩子等于y,返回1.接下来会很骚,还请注意.*/
}my_;//开一棵名字叫做my_的splay树.当然要取名为其他东西也是可以的.

注:接下来为了巩固splay的知识,我都直接在代码框里手打代码,如果出现错误还望大家指出.
如果你也初学splay,贺代码请谨慎,出现错误,我不背这个锅.

push_up

void push_up(int u){sz[u]=sz[ls(u)]+sz[rs(u)]+cnt[u];/*这个操作解释不用很多,就是左孩子的子树大小+右孩子的子树大小+节点u包含相同字的个数.*/}

rotate

这个直接把代码放上来只有神仙能看懂.我该画图解释.
注意:二叉查找树满足左孩子权值小于节点权值,而右孩子权值严格大于节点的权值,以下图片中显现的是节点编号而不是节点权值.

看上图,我们现在要把节点4" role="presentation" style="position: relative;">444旋转到 2 2 2的位置,为了使它仍然满足二叉查找树的性质,我们来操作一下.
旋转完的树应该是这样的.

给每个节点代个权值进去会发现它仍然满足二叉树的性质.
我们不妨来找旋转的规律.

首先假设要旋转的节点是x,它的父亲是y,祖父是z.
我们先找出x与y的关系(左右儿子).
然后必然满足的条件:
1.x的x相对于y的子树不变.
2.x的相对于x相对于y的子树变成了y的x相对于y方向的子树.(仔细思考)
3.x的父亲变成z,y变成相对于x原来相对于y的儿子.

接下来写出旋转的代码,可结合上图分析使用.

void zhuan(int x){//rotateint y=fa[x],z=fa[y],k=ws(y,x);//取父亲和祖父,求x是y的哪一个儿子.ch[ws(z,y)][z]=x,fa[x]=z;/*z的y原来相对于z的儿子变成x,x的父亲变成z.*/ch[k][y]=ch[k^1][x],fa[ch[k^1][x]]=y;/*x的与原来x相对于y的方向相反的儿子变成y的x相对于y方向的儿子,同理赋值父亲.*/ch[k^1][x]=y,fa[y]=x;/*y变成x的与原来x相对于y相反方向的儿子,y的父亲变成x.*/push_up(y),push_up(x);/*push_up一下.*/}

给你们10分钟时间听歌思考.
……

splay

该树最核心的操作之一────splay操作出现了.
如果你能够理解上面的旋转操作我们就直接上代码了.

void splay(int x,int g){//将x旋转成为g的儿子,如果g是0则将x旋转到根. for (;fa[x]^g;zhuan(x)){//最后一定会旋转一次x int y=fa[x],z=fa[y];if (z^g) zhuan(ws(z,y)^ws(y,x)?x:y);//如果z不是根就额外转一次./*通过判断y是z的哪个儿子和x是y的哪个儿子的关系决定是旋转y还是x.其实分类讨论zig,zag也是这样的算法,非常麻烦,用这个就很简单了.*/ }if (!g) rt=x;//如果g等于0将x设为根. }
/*这些操作的意图都是使树变得平衡.*/

find

把维护splay平衡的代码写完了,终于到询问的时候了.
这个操作能够找到存放大小为x" role="presentation" style="position: relative;">xxx的数字的那个节点,并把这个节点splay到根.

void find(int x){int u=rt;if (!u) return;//rt为0,该树中没有点,直接跳出.for (;ch[x>val[u]][u]&&val[u]^x;)u=ch[x>val[u]][u];/*根据二叉查找树的性质,根据x关于左右儿子存放的值的关系在左右儿子中查找.注意特判x恰好和u存放的值相等的情况.*/splay(u,0);//将u节点splay到根,为求x是第几大的数字做铺垫.}

insert

插入一个节点.

void insert(int x){int u=rt,nf=0;//nf是现在u的父亲.for (;u&&val[u]^x;) nf=u,u=ch[x>val[u]][u];//按顺序找x应该在的位置.if (u) ++cnt[u];//如果已经存在x,将cnt++.else{//不存在,新开一个节点.u=++tot;if (nf) ch[x>val[nf]][nf]=u;ls(u)=rs(u)=0;fa[u]=nf,val[u]=x,cnt[u]=1,sz[u]=1;}splay(u,0);//不要忘记splay到根,保持树的平衡性.}

next

前驱和后继.

int next(int x,int f){//f前驱为0,后继为1find(x);int u=rt;if (val[u]>x&&f||val[u]<x&&!f) return u;/*find过后val[u]如果不等于x,它一定是最接近x的那个数,如果此时val[u]比x大或者小满足要求的前或者后,就可以直接判断前驱后继.*/u=ch[f][u];//先往u向f的那个方向跳一个儿子.for (;ch[f^1][u];) u=ch[f^1][u];/*接下来一直往反方向跳,就可以得到比x大的最小的数或者比x小的最大的数.*/return u;}

delete

借助next可以完成删除操作.

void del(int x){int lat=next(x,0),net=next(x,1);/*定义last为x的前驱,next为x的后继.*/splay(lat,0),splay(net,lat);/*把last转到根,next转到last的右儿子.显然一定是转到右儿子.*/int nde=ls(net);//那么值为x的节点此时应该是next的左儿子.if (cnt[nde]>1){//cnt[nde]>1的话可以只是删掉一个.cnt[nde]--;splay(nde,0);//转到根节点.}else ls(net)=0; }

findkth

最后是找第k大值.

int kth(int x){int u=rt;if (sz[u]<x) return 0;/*如果sz[u]<x说明整棵树里没有k个值,也当然没有第k大.*/for (;;){int y=ls(u);if (x>sz[y]+cnt[u]){x-=sz[y]+cnt[u];u=rs(u); }else if (sz[y]>=x) u=y;else return val[u];}//这一部分分类讨论估计也不用讲了.}

main

main就很简单了.

int main(){
my_.insert(-1e7-4);
my_.insert(1e7+4);
n=read();
for (;n--;){int op=read();switch(op){case 1: my_.insert(read()); break;case 2: my_.del(read()); break;case 3: my_.find(read()),write(my_.sz[my_.ls(rt)]),pl; break;case 4: write(my_.kth(read()+1)),pl; break;case 5: write(my_.val[my_.next(read(),0)]),pl; break;case 6: write(my_.val[my_.next(read(),1)]),pl; break;}}
}

预告:接下来会写LCT.
谢谢大家.

关于splay的一些说明相关推荐

  1. 二逼平衡树——树套树(线段树套Splay平衡树)

    题面 Bzoj3196 解析 线段树和Splay两棵树套在一起,常数直逼inf,但最终侥幸过了 思路还是比较简单, 在原数组维护一个下标线段树,再在每一个线段树节点,维护一个对应区间的权值Splay. ...

  2. 简析平衡树(三)——浅谈Splay

    前言 原本以为\(Treap\)已经很难了,学习了\(Splay\),我才知道,没有最难,只有更难.(强烈建议先去学一学\(Treap\)再来看这篇博客) 简介 \(Splay\)是平衡树中的一种,除 ...

  3. AVL树、splay树(伸展树)和红黑树比较

    AVL树.splay树(伸展树)和红黑树比较 一.AVL树: 优点:查找.插入和删除,最坏复杂度均为O(logN).实现操作简单 如过是随机插入或者删除,其理论上可以得到O(logN)的复杂度,但是实 ...

  4. bzoj1251: 序列终结者 (splay)

    splay可以用于维护序列,比如noi的维修序列,比如这道 发现当时splay没写总结,也没题解 然后重新写splay竟然耗了一个晚上 结果是因为max[0]没有附最小值!!血一样的教训 最后祭出in ...

  5. BZOJ 1503 郁闷的出纳员(splay)

    题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1503 题意:给出一个数列(初始为空),给出一个最小值Min,当数列中的数字小于Min时自动 ...

  6. [BZOJ1503]郁闷的出纳员(Splay)

    Description OIER公司是一家大型专业化软件公司,有着数以万计的员工.作为一名出纳员,我的任务之一便是统计每位员工的工资.这本来是一份不错的工作,但是令人郁闷的是,我们的老板反复无常,经常 ...

  7. Splay ---- 2018牛客多校第三场 区间翻转搞区间位移 或者 rope可持久化块状链表

    题目链接 题目大意: 就是每次把牌堆中若干个连续的牌放到堆顶,问你最后牌的序列. 解题思路: Splay 区间翻转的模板题: 对于一个区间[1,2,3,4,5,6,7,8][1,2,3,4,5,6,7 ...

  8. splay + 线段树 ---- P3765总统选举 [带修改的动态区间众数 摩尔投票+n棵splay]

    题目链接 题目大意: 解题思路: 1.摩尔投票法: 题意是找n个数内出现次数大于n/2的数 保证存在这个数用的方法叫做摩尔投票法 首先我们注意到这样一个现象: 在任何数组中,出现次数大于该数组长度一半 ...

  9. Splay ---- 区间翻转 区间最大值 区间加 P4146 序列终结者

    题目链接 题目大意: 解题思路: 这是一道很入门的Splay的题目 但是第一次写有很多坑点 首先是maxmaxmax值的更新:就是因为这个点的最小值是会出现负数负数的,当你左右儿子有一个没有的话,那么 ...

  10. Splay ---- 文艺平衡树区间翻转的建树模式

    Splay 的区间操作 这个splay已经不是平常的权值splay了, 每个节点维护区间的位置取决于它在树里面的位置 首先这课splay中序遍历的结果就是你维护的整个区间的数值!!注意这个中序遍历很重 ...

最新文章

  1. 查看tensorflow pb模型文件的节点信息
  2. 经典C语言程序100例之六七
  3. Hibernate中的inverse属性和cascade属性
  4. 数据库表操作、数据类型及完整性约束
  5. linux系统基础与应用,Linux操作系统:基础、原理与应用
  6. LeetCode MySQL 614. 二级关注者
  7. 排除jar_Gradle排除依赖关系
  8. 经典面试题(41):以下代码将输出的结果是什么?
  9. 0918类对象重载,作业2
  10. [转]MySQL游标特性
  11. gridview无法显示完整
  12. idc机房运维巡检_24小时保姆式机房巡检设备设计-机房巡检机器人设计
  13. python在线运行编程工具模板源码[网页版]
  14. 有定位的含有 input的弹框,在有些手机光标乱跳
  15. 服务器-----tomact服务器工作原理及其工作报错
  16. Unity-使用UPR资源检测工具AssetChecker-Win进行本地资源检测
  17. 深度学习epochs、batch、batch_size和iteration的区分
  18. shell脚本小游戏
  19. androidstudio的语音唤醒功能
  20. python实现贪吃蛇小游戏

热门文章

  1. 数据库连接池druid 的jar包官网下载-最新版下载
  2. 信息流广告 html5,5分钟让你看懂“信息流广告出价”是怎么回事
  3. java输入值按回车下一个_java 如何 按回车 光标换到下个输入框
  4. JS返回上一页-JS返回下一页-JS页面跳转
  5. 数据可视化Matplotlib-中
  6. [Setting]win7下运行exe失败:应用程序无法启动,因为应用程序的并行配置不正确
  7. Android | 说说Presentation
  8. initramfs学习
  9. C++ COM组件编写初探(上)
  10. 猫和老鼠服务器未响应是怎么回事,猫和老鼠手游怎么玩不了?游戏设置及异常解决方法[多图]...