Splay浅谈
Splay是众多平衡树之一,它的功能十分强大,但常数极大。在LCT和许多数据结构中都能用到。
Splay的核心操作,就是rotate。为了使树不是一条链,而是平衡的,我们需要旋转来维护形态。理论很简单,下面来看一下如何实现。
图片转自洛谷
我们注意到,旋转之后,这棵树依然保持着正常的大小关系。
来尝试着写代码。
观察到,y的c儿子依旧是y的右儿子,而x,y的父子关系发生了改变,x的左儿子不变,x的右儿子变成了y的左儿子。
这是右旋。
明显的,我们发现不只有这一种情况。单旋的Splay,会被出题人的构造数据卡成链。我们先来处理双旋操作,再来处理链。
Rotate:
inline void rotate(node *x){
node *y=x->fa,*z=y->fa;int k=y->ch[1]==x;
z->ch[z->ch[1]==y]=x;x->fa=z;
y->ch[k]=x->ch[k^1];x->ch[k^1]->fa=y;
x->ch[k^1]=y;y->fa=x;pushup(y);pushup(x);
}
注意,结构体里面存了x节点的父亲,左右儿子,值,子树大小以及此处值相同的节点个数。
其中,k=1表示y的右儿子是x,这是逻辑表达式。
理解了rotatw操作,开始讲一下它的核心:Splay操作。
还记得我说的单旋吗?在一些情况下,如果你只旋转当前节点,会依旧成为一条链。
我们如何避免这件事呢?
我们观察到,当x,x的fa以及grandfa在一条线上时,转x是无意义的。
所以,我们要rotate(y).
就这么简单。
这样一直到x转到目标点。
代码:
inline void splay(node *x,node *goal){
node *y,*z;
while(x->fa!=goal){
y=x->fa,z=y->fa;
if(z!=goal)(z->ch[1]==y)^(y->ch[1]==x)?rotate(x):rotate(y);
rotate(x);
}if(goal==null)rt=x;
}
注意,此处我用的是指针,所以不是!goal,而是goal==null.
下面,就Splay模板开始讲解。
首先,插入操作。
我们插入一个点,首先判断,如果它是一棵空树,那就把树的根节点新建出来(指针),值为插入的值,return即可。
否则,往下沉。
有两种可能:在树中找的过程中,如果有值一样的,就把x旋转到根,并且把x的num++.
否则,新建节点,记录最后找到的点。
最后找到的点是新建点的father.
插入的值是新建点的值。
插入完成。
代码有些冗长,注意理解背诵。
inline node *new_(){
node *x=new node;
x->ch[0]=x->ch[1]=x->fa=null;x->siz=x->num=1;return x;
}
inline void insert(int v){
node *x=rt;
if(x==null){rt=new_();rt->v=v;return;}
node *fa=null;
while(x!=null){
fa=x;if(v!=x->v)x=x->ch[v>=x->v];
else {splay(x,null);x->siz++;x->num++;return;}
}x=new_();fa->ch[v>=fa->v]=x;
x->fa=fa;x->v=v;
splay(x,null);
}
下一步操作,删除(del,Del)
首先,我们知道,删除的值一定在树中。
于是,我们从根开始搜索。
找到之后,对于这个点,进行del.
先把这个点(x)splay到根,并且把它的子树大小(siz)和同值节点数量(num)--.
这时,有两种结果。
首先,于这个点相同值的点还有。
这样,直接return即可。
否则,如果这棵树只有这一个点,那就把根赋值为null,然后return即可。
否则,顺着x的左右孩子向下找。
找到最后的节点,把它旋转到x的孩子上。
这需要更新x了,也就是删除。
x的孩子认y(转上来的节点)做爹,y是根,爹为null,delete(x)即可。
删除完成。
代码:
inline void del(node *x){
splay(x,null);x->siz--;x->num--;int k;
if(x->num)return;
if(x->ch[0]!=null)k=0;
else if(x->ch[1]!=null)k=1;
else {rt=null;delete(x);return;}
node *y=x->ch[k];
while(y->ch[k^1]!=null)y=y->ch[k^1];
splay(y,x);y->ch[k^1]=x->ch[k^1];
x->ch[k^1]->fa=y;y->fa=null;rt=y;
delete(x);
}inline void Del(int v){
node *x=rt;
while(x!=null){
if(v!=x->v)x=x->ch[v>=x->v];
else {del(x);return;}
}
}
然后,就是最熟悉的求排名,求排名为k的数,以及前驱,后继。
rank:首先找v是否存在。不存在输出0.
如果存在,那么它的排名就是当前点的左孩子的子树大小+1.
完成。
求排名为k的数:
定义c为当前点的左子树大小。
如果当前点的左子树大小超过了k,说明它不是要找的点,向它的左子树找。
如果c<k了,说明找到了。
k就把c和当前点的num减掉。
如果k<0了,输出当前x的值即可。
否则,找x的右子树。
完成。
一起说前驱后继吧。
前驱,小于v的最大数。
后继,大于v的最小数。
从根开始搜索,定义一个极值用来更新。
这个不好阐述,看代码理解吧。
上代码:
inline int rank(int v){
node *x=rt;
while(x!=null){
if(v>x->v)x=x->ch[1];
else if(v<x->v)x=x->ch[0];
else{splay(x,null);return x->ch[0]->siz+1;}
}return 0;
}inline int kth(int k){
node *x=rt;int c;
while(x!=null){
c=x->ch[0]->siz;
if(c>=k)x=x->ch[0];
else{
k-=c+x->num;
if(k<=0){splay(x,null);return x->v;}
x=x->ch[1];
}
}return 0;
}
inline int pre(int v){
node *x=rt;int ans=-inf;
while(x!=null){
if(v>x->v)ans=max(ans,x->v),x=x->ch[1];
else x=x->ch[0];
}return ans;
}
inline int last(int v){
node *x=rt;int ans=inf;
while(x!=null){
if(v<x->v)ans=min(ans,x->v),x=x->ch[0];
else x=x->ch[1];
}return ans;
}
至此,Splay告一段落。
Splay被称为序列之王,它还可以实现许多功能。
下次讲树套树的时候,一并说一下。
注:模板来自dalao——wjr.
Splay浅谈的更多相关文章
- 浅谈splay(点的操作)
浅谈splay(点的操作) 一.基本概念 splay本质:二叉查找树 特点:结点x的左子树权值都小于x的权值,右子树权值都大于x的权值 维护信息: 整棵树:root 当前根节点 sz书上所有结点编号 ...
- Lct浅谈
Lct浅谈 1.对lct的认识 首先要知道$lct$是什么.$lct$的全称为$link-cut-tree$.通过全称可以看出,这个数据结构是维护树上的问题,并且是可以支持连边断边操作.$lct$ ...
- 浅谈 Fragment 生命周期
版权声明:本文为博主原创文章,未经博主允许不得转载. 微博:厉圣杰 源码:AndroidDemo/Fragment 文中如有纰漏,欢迎大家留言指出. Fragment 是在 Android 3.0 中 ...
- 浅谈 LayoutInflater
浅谈 LayoutInflater 版权声明:本文为博主原创文章,未经博主允许不得转载. 微博:厉圣杰 源码:AndroidDemo/View 文中如有纰漏,欢迎大家留言指出. 在 Android 的 ...
- 浅谈Java的throw与throws
转载:http://blog.csdn.net/luoweifu/article/details/10721543 我进行了一些加工,不是本人原创但比原博主要更完善~ 浅谈Java异常 以前虽然知道一 ...
- 浅谈SQL注入风险 - 一个Login拿下Server
前两天,带着学生们学习了简单的ASP.NET MVC,通过ADO.NET方式连接数据库,实现增删改查. 可能有一部分学生提前预习过,在我写登录SQL的时候,他们鄙视我说:“老师你这SQL有注入,随便都 ...
- 浅谈WebService的版本兼容性设计
在现在大型的项目或者软件开发中,一般都会有很多种终端, PC端比如Winform.WebForm,移动端,比如各种Native客户端(iOS, Android, WP),Html5等,我们要满足以上所 ...
- 浅谈angular2+ionic2
浅谈angular2+ionic2 前言: 不要用angular的语法去写angular2,有人说二者就像Java和JavaScript的区别. 1. 项目所用:angular2+ionic2 ...
- iOS开发之浅谈MVVM的架构设计与团队协作
今天写这篇博客是想达到抛砖引玉的作用,想与大家交流一下思想,相互学习,博文中有不足之处还望大家批评指正.本篇博客的内容沿袭以往博客的风格,也是以干货为主,偶尔扯扯咸蛋(哈哈~不好好工作又开始发表博客啦 ...
随机推荐
- steam 数据转换
目录 数组和集合互转 数组转集合 方法一 遍历 方法二 asList 方法三 steam 集合转数组 方法一 循环 方法二 toArray 方法三 steam 小结 string转为Character ...
- Tomcat源码分析(下载、启动)
1.下载Tomcat源代码: https://tomcat.apache.org/download-80.cgi 2. 解压以及创建必要目录和配置 解压.新建catalina-home目录,同时将目录 ...
- 5.AVStream和AVCodecParameters
AVStream和AVCodecParameters 说明: AVStream 结构表示当前媒体流的上下文,着重于所有媒体流共有的属性(并且是在程序运行时才能确定其值)和关联其他结构的字段. 其中co ...
- 分布式事务框架.NetCore CAP总结
来自CAP原作者yang-xiaodong的原理图: 本文撰写者:cmliu,部分内容引用自官方文档,部分内容待更新# .NetCore CAP # 1,简介 CAP 是一个遵循 .NET Stand ...
- [转载] 微软发布 SURFACE DUO ANDROID SDK 和模拟器
模拟器截图 微软今天发布了双屏折叠设备 Surface Duo Android 开发工具(SDK 和模拟器),Windows 10X 开发工具和模拟器之后 2 月 11 日发布,并宣布了新的针对双屏体 ...
- jzoj 6798. 【2014广州市选day2】regions
Description 在平面上堆叠着若干矩形,这些矩形的四边与平面X坐标轴或Y坐标轴平行.下图展示了其中一种情况,3个矩形的边将平面划分成8个区域: 下面展示了另一种稍稍复杂一些的情况: 你的任务是 ...
- 安装python3,配置pycharm
1.下载最新版python3 首先去python官网下载python3的源码包,网址:https://www.python.org/ 进去之后点击导航栏的Downloads,也可以鼠标放到Downlo ...
- [Java核心技术]第九章-集合(映射-HashMap&TreeMap)
HashMap 基本操作 HashMap<Integer,Integer> firstAccurMap=new HashMap<Integer,Integer>(); firs ...
- 使用swagger遇到的问题
1.定义全局的请求参数时, defaultValue不能是中文,不然一直是请求中 ParameterBuilder userName = new ParameterBuilder(); Paramet ...
- jenkins在windows系统及linux环境安装
一.下载 jenkins是当前持续集成(Continuous integration,简称 CI)的主流工具,在项目中得到了广泛的推广和应用. 下载地址: https://jenkins.io/dow ...