Splay伸展树学习笔记
Splay伸展树
有篇Splay入门必看文章 —— CSDN链接
经典引文
Tree Rotation

Splaying
- 节点x是父节点p的左孩子还是右孩子
- 节点p是不是根节点,如果不是
- 节点p是父节点g的左孩子还是右孩子
Zig Step

Zig-Zig Step

Zig-Zag Step

应用
自学笔记
今天开始自己动手写Splay。身边的小伙伴大多是用下标和数组来维系各个结点的联系,但是我还是一如既往的喜欢C++的指针(❤ ω ❤)。
结点以一个struct结构体的形式存在。
struct node {
int value;
node *father;
node *son[];
node (int v = , node *f = NULL) {
value = v;
father = f;
son[] = NULL;
son[] = NULL;
}
};
其中,son[0]代表左儿子,son[1]代表右儿子。
用一个函数来判断子节点是父节点的哪个儿子。
inline bool son(node *f, node *s) {
return f->son[] == s;
}
返回值就是son[]数组的下标,这个函数很方便。
最关键的是旋转操作,有别于常见的zig,zag旋转,我喜欢用一个函数实现其两者的功能,即rotate(x)代表将x旋转到其父节点的位置上。
inline void rotate(node *t) {
node *f = t->father;
node *g = f->father;
bool a = son(f, t), b = !a;
f->son[a] = t->son[b];
if (t->son[b] != NULL)
t->son[b]->father = f;
t->son[b] = f;
f->father = t;
t->father = g;
if (g != NULL)
g->son[son(g, f)] = t;
else
root = t;
}
函数会自行判断x实在父节点的左儿子上还是右儿子上,并自动左旋或右旋。这里要注意改变祖父结点的儿子指针,以及结点的父亲指针,切忌马虎漏掉。同时还要事先做好特判,放止访问非法地址。这里用指针相较于用下标的一个好处就是,如果你不小心访问了NULL即空指针,也是下标党常用的0下标,指针写法一定会RE,而下标写法可能就不会崩溃,因而不易发现错误,导致一些较为复杂而智障的错误。
然后是核心函数——Splay函数,貌似也有人叫Spaly的样子,然而我并没有考证什么。Splay(x,y)用于将x结点旋转到y结点的某个儿子上。特别地,Splay(x,NIL)代表将x旋转到根节点的位置上。根节点的父亲一般是NIL或0。
inline void spaly(node *t, node *p) {
while (t->father != p) {
node *f = t->father;
node *g = f->father;
if (g == p)
rotate(t);
else {
if (son(g, f) ^ son(f, t))
rotate(t), rotate(t);
else
rotate(f), rotate(t);
}
}
}
这里值得注意的是两种双旋。如果t(该节点),f(父亲节点),g(祖父节点)形成了一条单向的链,即[右→右]或[左→左]这样子,那么就先对父亲结点进行rotate操作,再对该节点进行rotate操作;否则就对该节点连续进行两次rotate操作。据称单旋无神犇,双旋O(logN),这句话我也没有考证,个人表示不想做什么太多的探究,毕竟Splay的复杂度本来就挺玄学的了,而且专门卡单旋Splay的题也没怎么听说过。对了,这个双旋操作和AVL的双旋是不是有那么几分相似啊,虽然还是不太一样的吧,好吧其实也不怎么像╮(╯-╰)╭。
接下来谈谈插入操作。插入操作就和普通的二叉搜索树类似,先找到合适的叶子结点,然后在空着的son[]上新建结点,把值放入。不同的是需要把新建的结点Splay到根节点位置,复杂度需要,不要问为什么。
inline void insert(int val) {
if (root == NULL)
root = new node(val, NULL);
for (node *t = root; t; t = t->son[val >= t->value]) {
if (t->value == val) { spaly(t, NULL); return; }
if (t->son[val >= t->value] == NULL)
t->son[val >= t->value] = new node(val, t);
}
}
注意,这个插入函数实现的是非重集合。
与之对应的就是删除操作,相对的复杂一些。删除一个元素,需要先在树中找到这个结点,然后把这个结点Splay到根节点位置,开始分类讨论。如果这个结点没有左儿子(左子树),直接把右儿子放在根的位置上即可;否则的话就需要想方设法合并左右子树:在左子树种找到最靠右(最大)的结点,把它旋转到根节点的儿子上,此时它一定没有右儿子,因为根节点的左子树中不存在任何一个元素比它更大,那么把根节点的右子树接在这个结点的右儿子上即可。
inline void erase(int val) {
node *t = root;
for ( ; t; ) {
if (t->value == val)
break;
t = t->son[val > t->value];
}
if (t != NULL) {
spaly(t, NULL);
if (t->son[] == NULL) {
root = t->son[];
if (root != NULL)
root->father = NULL;
}
else {
node *p = t->son[];
while (p->son[] != NULL)
p = p->son[];
spaly(p, t); root = p;
root->father = NULL;
p->son[] = t->son[];
if (p->son[] != NULL)
p->son[]->father = p;
}
}
}
相较于insert()确实复杂了不少。
以上就是Splay的框架了,是Splay必不可少的部分,在此基础上可以加入许多新的功能。
例如,手动实现垃圾回收,这样新建结点的常数会小很多,毕竟C++的new是很慢的。
node tree[siz], *stk[siz]; int top;
inline node *newnode(int v, node *f) {
node *ret = stk[--top];
ret->size = ;
ret->value = v;
ret->father = f;
ret->son[] = NULL;
ret->son[] = NULL;
ret->reverse = false;
return ret;
}
inline void freenode(node *t) {
stk[top++] = t;
}
有的时候需要我们维护子树大小。
inline int size(node *t) {
return t == NULL ? : t->size;
}
inline void update(node *t) {
t->size = ;
t->size += size(t->son[]);
t->size += size(t->son[]);
}
简单,安全。
维护区间翻转的时候需要用到打标记的方式。
inline bool tag(node *t) {
return t == NULL ? false : t->reverse;
}
inline void reverse(node *t) {
if (t != NULL)
t->reverse ^= true;
}
inline void pushdown(node *t) {
if (tag(t)) {
std::swap(t->son[], t->son[]);
reverse(t->son[]);
reverse(t->son[]);
t->reverse ^= true;
}
}
还有更为简洁的rotate函数。
inline void connect(node *f, node *s, bool k) {
if (f == NULL)
root = s;
else
f->son[k] = s;
if (s != NULL)
s->father = f;
}
inline void rotate(node *t) {
node *f = t->father;
node *g = f->father;
bool a = son(f, t), b = !a;
connect(f, t->son[b], a);
connect(g, t, son(g, f));
connect(t, f, b);
update(f);
update(t);
}
@Author: YouSiki
Splay伸展树学习笔记的更多相关文章
- zkw线段树学习笔记
zkw线段树学习笔记 今天模拟赛线段树被卡常了,由于我自带常数 \(buff\),所以学了下zkw线段树. 平常的线段树无论是修改还是查询,都是从根开始递归找到区间的,而zkw线段树直接从叶子结点开始 ...
- 仙人掌&圆方树学习笔记
仙人掌&圆方树学习笔记 1.仙人掌 圆方树用来干啥? --处理仙人掌的问题. 仙人掌是啥? (图片来自于\(BZOJ1023\)) --也就是任意一条边只会出现在一个环里面. 当然,如果你的图 ...
- 【学时总结】◆学时·VI◆ SPLAY伸展树
◆学时·VI◆ SPLAY伸展树 平衡树之多,学之不尽也…… ◇算法概述 二叉排序树的一种,自动平衡,由 Tarjan 提出并实现.得名于特有的 Splay 操作. Splay操作:将节点u通过单旋. ...
- 线段树学习笔记(基础&进阶)(一) | P3372 【模板】线段树 1 题解
什么是线段树 线段树是一棵二叉树,每个结点存储需维护的信息,一般用于处理区间最值.区间和等问题. 线段树的用处 对编号连续的一些点进行修改或者统计操作,修改和统计的复杂度都是 O(log n). 基础 ...
- SPLAY,LCT学习笔记(一)
写了两周数据结构,感觉要死掉了,赶紧总结一下,要不都没学明白. SPLAY专题: 例:NOI2005 维修数列 典型的SPLAY问题,而且综合了SPLAY常见的所有操作,特别适合新手入门学习(比如我这 ...
- Splay 伸展树
废话不说,有篇论文可供参考:杨思雨:<伸展树的基本操作与应用> Splay的好处可以快速分裂和合并. ===============================14.07.26更新== ...
- [Splay伸展树]splay树入门级教程
首先声明,本教程的对象是完全没有接触过splay的OIer,大牛请右上角.. 首先引入一下splay的概念,他的中文名是伸展树,意思差不多就是可以随意翻转的二叉树 PS:百度百科中伸展树读作:BoGa ...
- Treap-平衡树学习笔记
平衡树-Treap学习笔记 最近刚学了Treap 发现这种数据结构真的是--妙啊妙啊~~ 咳咳.... 所以发一发博客,也是为了加深蒟蒻自己的理解 顺便帮助一下各位小伙伴们 切入正题 Treap的结构 ...
- Splay伸展树入门(单点操作,区间维护)附例题模板
Pps:终于学会了伸展树的区间操作,做一个完整的总结,总结一下自己的伸展树的单点操作和区间维护,顺便给未来的自己总结复习用. splay是一种平衡树,[平均]操作复杂度O(nlogn).首先平衡树先是 ...
随机推荐
- BZOJ 2190: [SDOI2008]仪仗队
2190: [SDOI2008]仪仗队 Time Limit: 10 Sec Memory Limit: 259 MBSubmit: 2689 Solved: 1713[Submit][Statu ...
- 运维利器-ClusterShell集群管理操作记录
在运维实战中,如果有若干台数据库服务器,想对这些服务器进行同等动作,比如查看它们当前的即时负载情况,查看它们的主机名,分发文件等等,这个时候该怎么办?一个个登陆服务器去操作,太傻帽了!写个shell去 ...
- div+css兼容 ie6_ie7_ie8_ie9_ie10和FireFox_Chrome等浏览器方法
1.div的垂直居中问题 vertical-align:middle; 将行距增加到和整个DIV一样高 line-height:200px; 然后插入文字,就垂直居中了.缺点是要控制内容不要换行 ...
- HTML5商城开发一 楼层滚动加载数据
对于楼层加载在以前只是个想法,从来没实现过,刚好项目中碰到,再此总结一下 场景:HTML5,局部商品列表信息滚动(局部滚动条) 1.通过jq设置subCategoryScroll的高度为屏幕显示高度( ...
- HTTP下密码的安全传输、OAuth认证
在复杂的web环境下,我们没有百分的把握保证信息在传输的过程中不被接货,那不是用明文如何告诉服务器自己的身份呢? 在一些高度通信安全的网络中,数据传输会使用HTTPS作为传输协议,但是通常情况下我们没 ...
- 使用JavaScript打印网页指定DIV区域
JavaScript打印函数myPrint(obj): JavaScript打印页面指定div区域原理:使用window.open()在浏览器打开一个新的页面(window), 使用 window.d ...
- codevs 1531 山峰
codevs 1531 山峰 题目描述 Description Rocky山脉有n个山峰,一字排开,从西向东依次编号为1, 2, 3, --, n.每个山峰的高度都是不一样的.编号为i的山峰高度为hi ...
- Openwrt dnsmasq 设置要点
之前设置dnsmasq,一直没有奏效,后来摸索了一下,初步发现它的原理: 正常的流程应该是像这样的,先由client来发送DNS请求到网关,然后网关的dnsmasq处理这个请求, 再根据设置决定如何处 ...
- 如何优化 FineUI 控件库的性能,减少 80% 的数据上传量!
在开始正文之前,请帮忙为当前排名前 10 唯一的 .Net 开源软件 FineUI 投一票: 投票地址: https://code.csdn.net/2013OSSurvey/gitop/codevo ...
- 开源Asp.Net Core小型社区系统
源码地址:Github 前言 盼星星盼月亮,Asp.Net Core终于发布啦!! Asp.Net发布时我还在上初中,没有赶上.但是Asp.Net Core我从beta版本便一直关注.最初项目名叫As ...