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

article/2025/11/9 15:48:58

首先声明,本教程的对象是完全没有接触过splay的OIer,大牛请右上角。。

PS:若代码有误,请尽快与本人联系,我会尽快改正

首先引入一下splay的概念,他的中文名是伸展树,意思差不多就是可以随意翻转的二叉树

PS:百度百科中伸展树读作:BoGang,不知道是不是因为和某位大牛有关系

先看一道题目:

skydec有n个数,每次他都会把一些数放进一些盒子里,由于skydec太傻×,所以他不能判断数的大小,现在他请求你帮他求盒子里的第K小数

输入:一个数n表示数的个数,一个数m表示操作的个数 (n<=m<=100000)

操作由2部分组成,简称为a和b,如果a=0,则表示将b放进盒子里,如果a=1,则表示询问盒子里的第b小数

输出:对于每次询问输出答案

由于是临时造的题目,就不造样例了,引用一句RZZ大神的话:题是死的,人是活的,出题人是懒的。

如果是普通的查询的话,只要排序一遍直接输出data[b]。但是这是动态的

这里就要引进二叉搜索树了,二叉搜索树我就不介绍了,这都不知道的话也没必要来读这篇教程了= =。对于一个节点,我们可以从根开始,一直左右子树搜寻,直到搜到一个排名为k的数,具体实现:find(node,k)表示在以node为根的树中查找第k小的树。

当k=size[leftson[node]]+1时,显然根结点就是答案

然后分类讨论,当k<size[leftson[node]]+1时,往左子树搜寻,即返回find(leftson[node],k)

当k>size[leftson[node]]时,往右子树搜寻,即返回find(rightson[node],k-1-size[leftson[node]])

至于插入过程,我就不说了,太简单了

long find(long x,long k)
{long leftsize=size[leftson[x]];if(k==leftsize+1)return x;if(k<leftsize+1) return find(leftson[x],k);else             return find(rightson[x],k-leftsize-1);
}

如果是随机数据的话,这题随便无压力过,但是skydec开始丧心病狂了,他插入的数是递减的,这样的话这种方法就会导致时间复杂度O(n^2)

那么现在就要引进传说中的splay树了!

splay树是通过不断的旋转来维持logn的平均复杂度的,他不用像AVL树严格平衡,也不用记录高度等平衡信息,是十分灵活的,唯一的缺点就是常数比其他平衡树要大一点

但是我初学时一直有一个疑问:splay是如何维持logn的复杂度的呢

由于splay是不断翻转的,所以就算某一时刻他成了一条链,也会马上变成另外的形态,通过这样不断地变换可以防止长期停留在链的状态

打个比方,如果你有100元,你可以在某个彩票上孤注一掷,也可以买50张彩票,由于孤注一掷可能会跪掉,所以肯定是买50张好,因为就算跪了,也不会赔太多钱

然后平均时间就是logn了

现在引入旋转这个概念

其中,我们是以X结点为主角的

现在来解释一下这一幅图

解释之前,肯定有人要问了:这旋转有个鸟用。其实这次旋转成功地将结点X上提了一步,其实splay的最终目标就是把某个结点提到他的某个祖先的下面。

右旋其实只需要三步:

1.将X的右子树B(如果有的话)作为Y的左子树,同时让B认Y作爹

2.设Z为原本Y结点的父亲,让X认Z做爹(如果Z存在的话),将X作为Z的儿子(是左是右得由Y是Z的左儿子还是右儿子决定,要左右一致)

3.将Y作为X的右子树,同时让Y认X作爹

下面贴个代码吧,希望看得懂

void right_rotate(long x)
{long y=father[x];long z=father[y];leftson[y]=rightson[x];if(rightson[x]!=0)father[rightson[x]]=y;//对图中B子树,即X的右子树的处理father[x]=z;if(z!=0){if(leftson[z]==y)leftson[z]=x;else rightson[z]=x;}rightson[x]=y;father[y]=x;
}


左旋同理,为了节省时间,就不多解释了,直接贴代码吧

void left_rotate(long x)
{long y=father[x];long z=father[y];rightson[y]=leftson[x];if(leftson[x]!=0)father[leftson[x]]=y;father[x]=z;if(z!=0){if(leftson[z]==y)leftson[z]=x;else rightson[z]=x;}leftson[x]=y;father[y]=x;
}

下面来一个合集

O(∩_∩)O~请允许我写一个装13的代码

void rotate(long x,int kind)
//kind=0表示左旋,kind=1表示右旋 ch[X][0..1]表示X的左儿子和右儿子
{long y=father[x];long z=father[y];ch[y][!kind]=ch[x][kind];if(!ch[x][kind])father[ch[x][kind]]=y;father[x]=z;if(!z)ch[z][ch[z][1]==y]=x;father[y]=x;ch[x][kind]=y;
} 
那么现在,最基础的左旋右旋解决了

开始splay树最最最最最核心的操作----------splay

splay(x,Ancestry)表示将x不停向上旋转,知道X成为结点Ancestry的子树 (Ancestry意为祖先,装13专用)

一般应用比较广泛的是将X结点Splay到根节点

下面放一下伪代码

void splay(long x,long Ancestry)
{while(father[x]!=Ancestry){各种旋转(~ o ~)~zZ} 
} 

众人:(啪!)你特么不是写了跟没写一样么

好吧,下面开始补全各种旋转

旋转分为2种情况,其中两种情况又总共分为6种情况

第一种情况,Ancestry是X的父亲的父亲(可以称作爷爷了。。)

然后又分为两种情况:

1.

其中有些结点我懒得话了,比如X的右兄弟

这种情况下,只需要执行一遍 right_rotate(x)即可

2.

这种情况下,只需要执行一遍left_rotate(x)

//-------------------------------------------------------------------------------------------------------------------------------//

以上两种都是Ancestry是X的爷爷的情况

下面两种是Ancestry还不是X的爷爷的情况

3.

当leftson[z]=y且rightson[y]=x时,只需要执行两部:left_rotate(x),   right_rotate(x)

4.

当rightson[z]=y且leftson[y]=x时,只需要:right_rotate(x),left_rotate(x)即可

五六两种情况合起来讨论:


当形成类似一条线时

左边的情况:right_rotate(y),right_rotate(x);

右边的情况:left_rotate(y);left_rotate(x);

至于为什么要先旋转Y结点,我也不知道

那么到此为止,6种旋转都介绍完了,下面贴一波代码

void splay(long x,long Ancestry)
{while(father[x]!=Ancestry){long y=father[x];long z=father[y];if(z==Ancestry){if(rightson[y]==x)left_rotate(x);else right_rotate(x);}else{if(rightson[z]==y&&rightson[y]==x){left_rotate(y);left_rotate(x);}else if(rightson[z]==y&&leftson[y]==x) {right_rotate(x);left_rotate(x);}else if(leftson[z]==y&&leftson[y]==x)  {right_rotate(y);right_rotate(x);}else                                   {left_rotate(x);right_rotate(x);}}}if(Ancestry==0)root=x;
} 

然后下面来一个装13版

void splay(long x,long Ancestry)
{while(father[x]!=Ancestry){long y=father[x];long z=father[y];if(z==Anc

http://chatgpt.dhexx.cn/article/X83GGA5u.shtml

相关文章

Splay Tree(伸展树)

二叉查找树&#xff08;Binary Search Tree&#xff09;能够支持多种动态集合操作。因此&#xff0c;在信息学竞赛中&#xff0c;二叉排序树起着非常重要的作用&#xff0c;它可以被用来表示有序集合、建立索引或优先队列等。 作用于二叉查找树上的基本操作的时间是与树的高度…

浅谈splay

例题&#xff1a; 给出一个长度为n序列a。 有m次操作&#xff0c;每次操作可以修改a[i]&#xff0c;在第i个数前插入一个数x&#xff0c;或查询区间[l,r]的最大值。 1≤n≤100000,1≤m≤100000。 强制在线 看到这道题最自然的反应就是用线段树&#xff0c;但有了插入操作线段树…

关于splay的一些说明

前言splay出现的背景它的操作 push_uprotatesplayfindinsertnextdeletefindkthmain 前言 暑假快过完了,大家感觉是不是很棒!gay房也体验过最新的断电模拟器了. 好吧,在开讲之前我还是得说一个.刀剑神域的外传实在太好看了! 除了精彩的战斗场面,神一般的人设和强大的剧情,比…

Splay入门详解

Splay入门详解 写在前面 听说平衡树是一种强大的数据结构&#xff0c;听同年级或高年级大佬们讲起来也感觉很牛笔的亚子&#xff0c;而最近XC又叫我们去学习一下 L C T LCT LCT&#xff01;&#xff1f; 又因为 S p l a y Splay Splay是学习 L C T LCT LCT的基础&#xff0c;…

【学习笔记】Splay

普通平衡树 模板题链接 1、引入 一种二叉树&#xff0c;这棵树满足任意一个节点&#xff0c;它的左儿子的权值<自己的权值<右儿子的权值 这种树叫做二叉查找树&#xff0c;这个概念应该在初赛中见过了吧 Splay就是利用这个结构来实现的 2、变量 模板题的7大变量 s…

平衡树详解(Splay)

引入 先看例题&#xff1a;&#xff08;洛谷 P3369 【模板】普通平衡树&#xff09; 您需要写一种数据结构&#xff0c;来维护一些数&#xff0c;其中需要提供以下操作&#xff1a; 1.插入 x x x 数 2.删除 x x x 数(若有多个相同的数&#xff0c;因只删除一个) 3.查询 x x…

Splay入门解析【保证让你看不懂(滑稽)】

upd&#xff1a;忽然发现直接在百度上搜索 splay s p l a y 就会搜索到我这篇文章&#xff1f;小蒟蒻瑟瑟发抖。 因为CSDN这边我基本不会看回复什么的&#xff0c;所以如果有问题还是请移步cnblogs里面吧 Cnblogs里面这篇文章的链接 BST真是神奇的东西。。。 而且种类好多呀…

Splay Tree

类别&#xff1a;二叉排序树 空间效率&#xff1a;O(n) 时间效率&#xff1a;O(log n)内完成插入、查找、删除操作 创造者&#xff1a;Daniel Sleator和Robert Tarjan 优点&#xff1a;每次查询会调整树的结构&#xff0c;使被查询频率高的条目更靠近树根。 注&#xff1a;所有…

【算法详解】splay的初步了解

qwq表示最近感受到了不停课的鸭梨啊&#xff0c;好难搞啊……算法太难学了……我好菜啊qwq 其实半个多月前就可以写这篇文章&#xff0c;不过由于时间紧张没写emmmmmm&#xff0c;不扯闲言乱语了&#xff0c;如果有什么写的不好的地方凑活一下吧2333333333 splay是一种可以使树…

splay详解

一&#xff1a;什么是splay&#xff08;伸展树&#xff09; 首先了解二叉排序树 二叉排序树或者是一棵空树&#xff0c;或者是具有下列性质的 二叉树&#xff1a; &#xff08;1&#xff09;若左子树不空&#xff0c;则左子树上所有结点的值均小于或等于它的 根结点的值&#…

Splay 总结基础精华

前言&#xff1a; 第一次学习Splay是2月份&#xff0c;打板子的时候是3月份&#xff0c;Ac是4月份&#xff0c;写这篇博客是6月初&#xff1b;原因是因为我竟然发现我忘了Splay的板子了&#xff01;很慌&#xff0c;必须总结一下&#xff01; 不敢说是最详细的&#xff0c;但希…

关系型数据库与非关系型数据库详解

关系数据库与非关系型数据库 一、数据库概述1、关系型数据库2、非关系型数据库 二、数据库区别1、数据存储方式不同2、扩展方式不同3、对事务性的支持不同 三、非关系型数据库产生背景四、Redis简介1、Redis 优点 五、Redis 安装部署六、Redis 命令工具&#xff08;1&#xff0…

关系型数据库RDS基本简介

OSS存放非结构化的数据&#xff0c;如&#xff1a;音频、视频、图片 rds存放结构化的数据&#xff0c;如&#xff1a;一张表&#xff0c; RDS产品概要 可以根据业务的需求进行弹性伸缩。 采用主从备份架构&#xff0c;三节点数据存储&#xff0c;具备高可用性和数据可靠性 …

2 关系型数据库是什么?

目录结构 关系型数据库基本概念结构化查询语言数据定义语言&#xff08;DDL&#xff09;数据查询语言&#xff08;Data Query Language, DQL&#xff09;数据操纵语言&#xff08;Data Manipulation Language, DML&#xff09;数据控制语言&#xff08;Data Control Language, …

常见的关系型数据库有哪些

Oracle Database&#xff1a;由Oracle公司开发和维护的商业关系型数据库&#xff0c;具有广泛的应用场景和功能。 MySQL&#xff1a;开源关系型数据库&#xff0c;常用于Web应用程序和小型企业应用。 Microsoft SQL Server&#xff1a;由Microsoft公司开发和维护的商业关系型…

Bezier曲线快速相交计算(含代码)

Bezier曲线快速相交计算 背景介绍算法思路解释和分析示例参考资料 背景介绍 很多时候&#xff0c;需要计算曲线段与曲线段是否有交点。常规的思路是直接联立方程求解。不过&#xff0c;直接求方程的解这种思路通常在计算上开销较大。 针对任意曲线&#xff0c;曲线的方程阶次…

js绘制贝塞尔曲线(Bézier-Curve)

贝塞尔曲线(Bzier curve)&#xff0c;又称贝兹曲线或贝济埃曲线&#xff0c;是应用于二维图形应用程序的数学曲线。一般的矢量图形软件通过它来精确画出曲线&#xff0c;贝兹曲线由线段与节点组成&#xff0c;节点是可拖动的支点&#xff0c;线段像可伸缩的皮筋&#xff0c;我们…

Bezier曲线与B-Spline曲线

微分几何基础 微分集合是用微分的方法来研究曲线的局部性质&#xff0c;如曲线的弯曲程度等。 一条可以用参数表示的三维曲线是一个有界点集&#xff0c;可以写成一个带参数的、连续的、单值的数学函数&#xff1a; { x x ( t ) y y ( t ) z z ( t ) 0 ⩽ t ⩽ 1 p p ( t…

初识贝塞尔(bezier)曲线

文章目录 资料援引贝塞尔曲线的用途一阶贝塞尔&#xff08;bezier&#xff09;曲线二阶贝塞尔&#xff08;bezier&#xff09;曲线三阶贝塞尔&#xff08;bezier&#xff09;曲线高阶贝塞尔&#xff08;bezier&#xff09;曲线三阶贝塞尔曲线求插值&#xff08;Slerp&#xff0…

贝塞尔曲线(Bezier Curve)原理、公式推导及matlab代码实现

1. 定义 贝塞尔曲线(Bezier curve)&#xff0c;又称贝兹曲线或贝济埃曲线&#xff0c;是应用于二维图形应用程序的数学曲线。一般的矢量图形软件通过它来精确画出曲线&#xff0c;贝兹曲线由线段与节点组成&#xff0c;节点是可拖动的支点&#xff0c;线段像可伸缩的皮筋&#…