M y f i r s t b l o g \color{white}{\rm My\ first\ blog} My first blog
写 给 新 手 , 大 佬 勿 喷 {\rm 写给新手,大佬勿喷} 写给新手,大佬勿喷
目录
- 前置知识
- Splay是什么
- 支持的操作
- 左旋
- 右旋
- 伸展
- 基本操作
- 前驱
- 后继
- 插入
- 删除
- 查某数排名
- 查排名为x的数
- 时间复杂度
- 例题
- 结语
前置知识
- 平衡树
- 二叉查找树
- 树上操作
- 指针
- 函数运用
- 基础数学知识
Splay是什么
Splay tree(伸展树)是一种 平衡树,由 Daniel Sleator 和 Robert Endre Tarjan 在1985年发明,用于保证 二叉查找树 的 尽量平衡1 ,同时维护二叉查找树的性质2,使得查找操作的时间复杂度变低3。
Splay依赖于 伸展操作,而 伸展操作 依赖于 旋转4操作 。相对于其他平衡树,Splay代码较为简单,应用范围广,实用性较强,时间复杂度还行,不需要记录用于平衡树的冗余信息。
数据结构 Code:
struct Node
{
long long val,size,sum;//节点的值、以此节点为根的子树的大小、相同值的个数
Node *lson,*rson,*fa;//其左儿子、右儿子、父亲的指针
}tree[1000000];
基本维护 Code :
Node *None,*root;//备用空节点和根节点
int kl=2;//数组大小
void shaobing()
{
Node *p=tree+1,*q=tree+2;
p->val=100000000;
q->val=-100000000;
p->size=2;
q->size=1;
p->sum=q->sum=1;
p->lson=q;
q->fa=p;
root=p;
}//新建两个哨兵,防止越界
void Update(Node *p)
{
if(p==None) return;//空就不用理他
p->size=p->sum;
if(p->lson!=None) p->size+=p->lson->size;
if(p->rson!=None) p->size+=p->rson->size;
}//更新操作,当一个节点的位置改变了或新插入了一个节点时,需要重新维护size和same
支持的操作
1.左旋
字面意思,就是将某个节点旋到它父亲节点的父亲节点位置,同时维护BST性质。
举个例子:
然后我们需要将 4 号节点左旋,也就是将 4 号节点旋到 2 号节点的父亲位置。
而 4 号节点不可能有三个儿子,所以我们将把 2 号节点下移,同时将 2 号节点的左儿子连至 2 号节点的右儿子 7 号节点, 4 号节点的右儿子连至 2 号节点,这样子就能维护 BST 性质。
于是就变成了这样:
Code :
void zig(Node *p)
{
Node *Fa=p->fa;//指向它的父亲节点
Fa->lson=p->rson;
if(p->rson!=None) p->rson->fa=Fa;
p->rson=Fa;
p->fa=Fa->fa;
if(Fa->fa!=None) //需要特判,否则会出错
{
if(Fa->fa->lson==Fa)Fa->fa->lson=p;
else Fa->fa->rson=p;
}
Fa->fa=p;
Update(Fa);//先更新儿子节点,再更新父亲节点
Update(p);
}
2.右旋
和左旋原理基本一样。上图(将 3 号节点旋至 5 号节点):
Code :
void zag(Node *p)
{
Node *Fa=p->fa;//指向它的父亲节点
Fa->rson=p->lson;
if(p->lson!=None) p->lson->fa=Fa;
p->lson=Fa;
p->fa=Fa->fa;
if(Fa->fa!=None) //需要特判,否则会出错
{
if(Fa->fa->lson==Fa)Fa->fa->lson=p;
else Fa->fa->rson=p;
}
Fa->fa=p;
Update(Fa);//先更新儿子节点,再更新父亲节点
Update(p);
}
3.伸展
伸展是Splay中最重要的操作,通俗的说, 它就是左旋和右旋外面套一层循环 ,通过指挥左旋和右旋来实现伸展,就好像人体通过扭动关节来伸懒腰。
伸展是将某一个节点通过旋转旋到另一个节点的儿子位置,如果是旋到根&#