splay详解(一)
前言
Spaly是基于二叉查找树实现的,
什么是二叉查找树呢?就是一棵树呗:joy: ,但是这棵树满足性质—一个节点的左孩子一定比它小,右孩子一定比它大
比如说

这就是一棵最基本二叉查找树
对于每次插入,它的期望复杂度大约是$logn$级别的,但是存在极端情况,比如9999999 9999998 9999997.....1这种数据,会直接被卡成$n^2$
在这种情况下,平衡树出现了!
Splay简介
Splay是平衡树的一种,中文名为伸展树,由丹尼尔·斯立特Daniel Sleator和罗伯特·恩卓·塔扬Robert Endre Tarjan在1985年发明的(mmp怎么又是tarjan)
它的主要思想是:对于查找频率较高的节点,使其处于离根节点相对较近的节点。
这样就可以保证了查找的效率
那么现在问题来了:
- 什么样的点是查找频率高的点?
这个玩意儿确实不好统计,但是你可以认为每次被查找的点查找频率相对较高,说白了就是你把每次查找到的点搬到根节点去
当然你也可以每次查找之后随机一个点作为根,于是Treaplay这种数据结构就诞生啦
- 怎么实现把节点搬到根这种操作?
这也是Splay这种数据结构所要实现的功能,接下来我们详细的介绍一下
Splay基本操作
rotate
首先考虑一下,我们要把一个点挪到根,那我们首先要知道怎么让一个点挪到它的父节点
情况1
当X是Y的左孩子

这时候如果我们让X成为Y的父亲,只会影响到3个点的关系
B与X,X与Y,X与R
根据二叉排序树的性质
B会成为Y的左儿子
Y会成为X的右儿子
X会成为R的儿子,具体是什么儿子,这个要看Y是R的啥儿子
经过变换之后,大概是这样

情况2
当X是Y的右孩子
本质上和上面是一样的,

变换后为

这两种代码单独实现都比较简单,我就不写了(实际上是我懒)
但是这两种旋转情况很类似,第二种情况实际就是把第一种情况的X,Y换了换位置
我们考虑一下能不能将这两种情况合并起来实现呢?
答案是肯定的
首先我们要获取到每一个节点它是它爸爸的哪个孩子,可以这么写
bool ident(int x) {
return tree[tree[x].fa].ch[] == x ? : ;
}
如果是左孩子的话会返回0,右孩子会返回1
那么我们不难得到R,Y,X这三个节点的信息
int Y = tree[x].fa;
int R = tree[Y].fa;
int Yson = ident(x); //x是y的哪个孩子
int Rson = ident(Y);
B的情况我们可以根据X的情况推算出来,根据^运算的性质,0^1=1,1^1=0,2^1=3,3^1=2,而且B相对于X的位置一定是与X相对于Y的位置是相反的
(否则在旋转的过程中不会对B产生影响)
int B = tree[x].ch[Yson ^ ];
然后我们考虑连接的过程
根据上面的图,不难得到
B成为Y的哪个儿子与X是Y的哪个儿子是一样的
Y成为X的哪个儿子与X是Y的哪个儿子相反
X成为R的哪个儿子与Y是R的哪个儿子相同
connect(B, Y, Yson);
connect(Y, x, Yson ^ );
connect(x, R, Rson);
connect函数这么写,挺显然的
void connect(int x, int fa, int how) { //x节点将成为fa节点的how孩子
tree[x].fa = fa;
tree[fa].ch[how] = x;
}
单旋函数就是这样了,利用这个函数就可以实现把一个节点搬到它的爸爸那儿了,
Splay
Splay(x,to)是实现把x节点搬到to节点
最简单的办法,对于x这个节点,每次上旋直到to
但是!
如果你真的这么写,可能会T成SB,出题人可能会构造数据把单旋卡成$n^2$,不要问我为什么!(其实是我不知道)
一个感性的理解是这样的
把一个点双旋到根,可以使得从根到它的路径上的所有点的深度变为大约原来的一半,其它点的深度最多增加2
或者你可以了解一下为啥单旋是错的

下面我们介绍一下双旋的Splay
这里的情况有很多,但是总的来说就三种情况
1.to是x的爸爸,
这样的话吧x旋转上去就好
update in 2018.2.19
这里可能写错了一个地方(其实也没有写错)
因为我们在双旋的时候会改变三个点的关系,为了方别写,所以我们开始的时候把to设置为to的爸爸
if (tree[tree[x].fa].fa == to) rotate(x);
2.x和他爸爸和他爸爸的爸爸在一条线上

这时候先把Y旋转上去,再把X旋转上去就好
else if (ident(x) == ident(tree[x].fa)) rotate(tree[x].fa), rotate(x);
3.x和他爸爸和他爸爸的爸爸不在一条线上

这时候把X旋转两次就好
总的代码:
void splay(int x, int to) {
to = tree[to].fa;
while (tree[x].fa != to) {
if (tree[tree[x].fa].fa == to) rotate(x);
else if (ident(x) == ident(tree[x].fa)) rotate(tree[x].fa), rotate(x);
else rotate(x), rotate(x);
}
}
后记
至此,Spaly的最核心最基本的操作已经讲解完毕
至于这玩意儿怎么用,以及能实现什么功能,且听下回分解
splay详解(一)的更多相关文章
- 在洛谷3369 Treap模板题 中发现的Splay详解
本题的Splay写法(无指针Splay超详细) 前言 首先来讲...终于调出来了55555...调了整整3天..... 看到大部分大佬都是用指针来实现的Splay.小的只是按照Splay的核心思想和原 ...
- splay详解(二)
前言 在上一节中,我们讲述了Splay的核心操作rotate与splay 本节我会教大家如何用这两个函数实现各种强大的功能 为了方便讲解,我们拿这道题做例题来慢慢分析 利用splay实现各种功能 首先 ...
- splay详解(三)
前言 上一节我们学习了splay所能解决的基本问题,这节我来讲一下splay怎么搞区间问题 实现 splay搞区间问题非常简单,比如我们要在区间$l,r$上搞事情,那么我们首先把$l$的前驱旋转到根节 ...
- 普通Splay详解
预备知识: 二叉搜索树(BST) 至于BST,随便看一下就可以, 我们知道二叉搜索树是O(logN)的,那我们为什么要用平衡树呢? 之前我们了解到,BST的插入是小的往左子树走,大的往右子树走,如果凉 ...
- Splay详解
平衡树实际很简单的 以下讲解都以Luogu P3369 [模板]普通平衡树为例 我不会带指针的Splay,所以我就写非指针型的Splay Splay是基于二叉查找树(bst)实现的 什么是二叉查找树呢 ...
- [转载]Splay Tree数组实现+详解
变量声明:f[i]表示i的父结点,ch[i][0]表示i的左儿子,ch[i][1]表示i的右儿子,key[i]表示i的关键字(即结点i代表的那个数字),cnt[i]表示i结点的关键字出现的次数(相当于 ...
- Link-Cut-Tree详解
图片参考YangZhe的论文,FlashHu大佬的博客 Link-Cut-Tree实际靠的是实链剖分,重链剖分和长链剖分珂以参考树链剖分详解 Link-Cut-Tree将某一个儿子的连边划分为实边,而 ...
- Linq之旅:Linq入门详解(Linq to Objects)
示例代码下载:Linq之旅:Linq入门详解(Linq to Objects) 本博文详细介绍 .NET 3.5 中引入的重要功能:Language Integrated Query(LINQ,语言集 ...
- 架构设计:远程调用服务架构设计及zookeeper技术详解(下篇)
一.下篇开头的废话 终于开写下篇了,这也是我写远程调用框架的第三篇文章,前两篇都被博客园作为[编辑推荐]的文章,很兴奋哦,嘿嘿~~~~,本人是个很臭美的人,一定得要截图为证: 今天是2014年的第一天 ...
随机推荐
- 配置 RIPv1 和 RIPv2
拓扑图 场景您是公司的网络管理员.您所管理的小型网络中包含三台路由器,并规划了五个网络.您需要在网络中配置RIP路由协议来实现路由信息的相互传输.最初使用的是RIPv1,后来发现RIPv2更有优势,于 ...
- SSIS - 8.FTP 任务
FTP全称为 File Transfer Protocol(文件传输协议),是通过TCP网络将文件从一个服务器传输到另一个服务器.在SSIS包中,FTP任务是用来实现FTP功能的. 一.创建FTP连接 ...
- docker 及 docker-compose 的快速安装和简单使用
本篇将使用 DaoCloud 源在 Ubuntu 上简单快速安装 docker 及 docker-compose 并添加了通过 Dockerfile 及 docker-compose.yml 使用 n ...
- emWin仪表界面设计,含uCOS-III和FreeRTOS两个版本
第7期:多功能仪表界面设计配套例子:V6-912_STemWin提高篇实验_多功能仪表界面设计(uCOS-III)V6-913_STemWin提高篇实验_多功能仪表界面设计(FreeRTOS) 例程下 ...
- Dubbo与Nginx区别
Dubbo的负载均衡已经是服务层面的了,和nginx的负载均衡还在http请求层面完全不同.至于二者哪个优秀,当然没办法直接比较. 涉及到负载均衡就涉及到你的业务,根据业务来选择才是最适合的. dub ...
- Java面试题:Hibernate的二级缓存与Hibernate多表查询
我们来看两个有关Java框架之Hibernate的面试题,这是关于Hibernate的常考知识点. 1.请介绍一下Hibernate的二级缓存 解题按照以下思路来回答: (1)首先说清楚什么是缓存: ...
- Android 实现卡片翻转的动画(翻牌动画)
Android 实现卡片翻转的动画(翻牌动画) 需求描述 点击卡片,卡片翻转过来显示内容. 点击左边的卡片,将卡片翻转显示右边的图片结果. 功能实现 因为要翻转所以使用动画来完成翻转的动画.动画分为两 ...
- [Bash]LeetCode195. 第十行 | Tenth Line
Given a text file file.txt, print just the 10th line of the file. Example: Assume that file.txt has ...
- [Swift]LeetCode206. 反转链表 | Reverse Linked List
Reverse a singly linked list. Example: Input: 1->2->3->4->5->NULL Output: 5->4-> ...
- [Swift]LeetCode402. 移掉K位数字 | Remove K Digits
Given a non-negative integer num represented as a string, remove k digits from the number so that th ...