splay那点奇诡的东东(填坑ing)
来源:互联网 发布:知乎粉丝排名 编辑:程序博客网 时间:2024/06/04 14:42
- 当然要在前面先哔一哔
- splay
- 没用的东西我们学来干嘛
- 极其脑残的旋转
- What is 旋转
- 怎么旋转
- left rotate
- left rotate code
- right rotate
- right rotate code
- left rotate
- 单旋
- more scientific rotate
- 双旋
- situation one
- situation two
- situation three and four
- splay code
- splay的查找
- find code
- 例题
- 例题1线段树最大值
- problem
- analysis
- 例题1线段树最大值
- 为什么blog的末尾不哔一哔呢
当然要在前面先哔一哔
(首先,不知道BST是什么的话……戳这里)
众所周知BST的期望复杂度为
人类的智慧是伟大的,这时,BBT们就该出场了
BBT(平衡树),有red black tree、AVL、替罪羊、treap、splay等
BBT采取了某些和谐的思想,把原本卡成链复杂度接近
接下来,该介绍今天的主角——splay了
splay
没用的东西我们学来干嘛?
splay,中文名伸展树、分裂树,BBT的一种,实际是BST的神奇优化 (英文名叫splay或者是splay,我不知道,所以都称作splay了)
splay可以
O(log2n) 时间插入、查找以及删除,速度快到飞起,用途广,代码复杂度不高其实真正的情况是,所有能用线段树做的题目都能用splay切掉!
比起其他BBT,splay不用记录某些东东,空间比其他BBT优
仅是用旋转来让整课树不退化成链,这点和treap有点像
- 但时间复杂度可是相当的不稳定……这个等会再解释
那就,不管那么多了
极其脑残的旋转
What is 旋转?
在放图片前说一下
splay旋转的目的是让一个节点通过旋转成为另一个节点的儿子节点,然后进行单点或区间操作 (最基础的两个旋转和treap的两个旋转是一样的,但是双旋……咳咳)
怎么旋转?
left rotate
左旋:把p的右儿子q旋转,使q成为p的父亲
左旋步骤:
把q的父亲标记为p的父亲(即q的祖父)
把p的父亲标记为q
此时q有三个儿子节点,为了保证splay tree是二叉树,再把q的左儿子B的父亲标记为p
(不懂就画个图,清晰明了)
由右图可知:A < P < B < Q < C,从右树转到左树,虽然节点位置发生了变化
但
left rotate code
void left_rotate(int x){ downdata(father[x]); downdata(x); int y=father[x],z=father[y]; father[x]=z,father[y]=x; if (z==0)root=x; else { if (tree[z][1]==y) { tree[z][1]=x; } else tree[z][2]=x; } size[y]=size[tree[y][1]]+size[tree[x][1]]+1; size[x]=size[x]+size[y]-size[tree[x][1]]; if (tree[x][1]) { father[tree[x][1]]=y; } tree[y][2]=tree[x][1]; tree[x][1]=y;}
right rotate
右旋:把q的左儿子p旋转,使p成为q的父亲
步骤、性质和左旋同理,这里不再多讲
right rotate code
void right_rotate(int x){ downdata(father[x]); downdata(x); int y=father[x],z=father[y]; father[x]=z,father[y]=x; if (z==0)root=x; else { if (tree[z][1]==y) { tree[z][1]=x; } else tree[z][2]=x; } size[y]=size[tree[y][2]]+size[tree[x][2]]+1; size[x]=size[x]+size[y]-size[tree[x][2]]; if (tree[x][2]) { father[tree[x][2]]=y; } tree[y][1]=tree[x][2]; tree[x][2]=y;}
单旋
我们在这里,需要把z旋转到x的儿子节点(当x是z的祖父的时候)
直接右旋z
直接左旋z
单旋一共只有两种情况 (其实旋转很简单……自己心神意会一下)
more scientific rotate
简单不?简单,不过您以为这就完了? OK经过一堆的单旋以后,splay树可能变成这样:
(可以自己思考思考为什么splay tree会变成这个狗(ノ=Д=)ノ┻━┻样)
那么,我们需要更加科学的方法
双旋
比如我们要把
如果现在
双旋的时间复杂度Tarjan证过均摊
(注意是均摊……均摊……均摊,不是期望!这就是splay时间复杂度不稳定的原因!)
双旋一共有四种情况
situation one
下图是把x旋到z以上的某个父亲节点
明显,先左旋y,再左旋x
situation two
下图是把z旋到x以上的某个父亲节点
同样的,先右旋y,再右旋z
上面两个是成一条链的情况
situation three and four
下图是把x旋到z以上的某个父亲节点
这里很显然,先左旋x再右旋x
情况四和情况三是差不多的,即z的左儿子是y、y的右儿子是x,双旋是先右旋x再左旋x
图就不用再贴了
splay code
单旋叫做spaly,双旋叫做splay,额貌似是这样?
哎算了不管那么多了 ̄3 ̄
反正我很确定的一点就是双旋的过程就被称作splay!
void splay(int x,int y){ if (x==y || x==0)return; while (father[x]!=y) { if (father[father[x]]==y) { if (tree[father[x]][1]==x) { right_rotate(x); } else left_rotate(x); } else { if (tree[father[father[x]]][1]==father[x] && tree[father[x]][1]==x) { right_rotate(father[x]); right_rotate(x); } else if (tree[father[father[x]]][1]==father[x] && tree[father[x]][2]==x) { left_rotate(x); right_rotate(x); } else if (tree[father[father[x]]][2]==father[x] && tree[father[x]][1]==x) { right_rotate(x); left_rotate(x); } else if (tree[father[father[x]]][2]==father[x] && tree[father[x]][2]==x) { left_rotate(father[x]); left_rotate(x); } } }}
splay的查找
splay的查找和普通BST的查找并没有什么卵区别
由于BST性质,比当前节点关键字小就往左边找,大就往右边找,刚刚好不就找到了嘛
恩设当前的节点为
设
可能说的有点反人类,我自己鼠绘了个图
find code
int find(int x,int y){ if (tree[x][1])downdata(tree[x][1]); if (tree[x][2])downdata(tree[x][2]); if (y==size[tree[x][1]]+1)return x; else { if (y<size[tree[x][1]]+1)return query(tree[x][1],y); else return query(tree[x][2],y-size[tree[x][1]]-1); }}
例题
例题万岁!
例题1:【线段树】最大值
problem
题目描述
在N(1<=N<=100000)个数A1…An组成的序列上进行M(1<=M<=100000)次操作,操作有两种:
(1)1 x y:表示修改A[x]为y;
(1)2 x y:询问x到y之间的最大值。
输入
第一行输入N(1<=N<=100000),表示序列的长度,接下来N行输入原始序列;接下来一行输入M(1<=M<=100000)表示操作的次数,接下来M行,每行为1
x y或2 x y输出
对于每个操作(2)输出对应的答案。
样例输入
5 1 2 3 4 5 3 2 1 4 1 3 5 2 2 4
样例输出
4 5
数据范围限制
提示
【限制】
保证序列中的所有的数都在longint范围内
analysis
线段树裸题,但我要用splay把它切了!
为什么blog的末尾不哔一哔呢?
- splay那点奇诡的东东(填坑ing)
- 点分治(高级的坑就要专心填)
- 关于点赞问题的填坑。
- 关于LCA、树上倍增、树链剖分和LCT(填坑ing)
- 嵌入式linux那点东东之整体框架
- 【模板篇】splay(填坑)+模板题(普通平衡树)
- 记点东东(resin配置的)
- 14年末留下点东东(补记)
- 关于js坑的那点事
- Ubuntu的那点小事(一)
- 那点代码, 谁都会写!------以后我会在博客中多加入一些思路方面的东东, 少写一些实际的代码
- automaticallyAdjustsScrollViewInsets那点坑
- (填坑)AD HOC - 临时的
- 记点东东
- 随便写点东东。。。
- 写点东东,
- 学习点新东东
- 东东的舞会 (小根堆)
- [SCOI2011]糖果 bzoj2330 洛谷 P3275
- eclipse中如何关联JDK源码
- Spring 发送邮件 (3) Spring使用模板Freemarker
- Asp.net+Vue2构建简单记账WebApp之六(vue.js构建记账统计页面)
- 文章标题
- splay那点奇诡的东东(填坑ing)
- UDP -服务器
- kernel中的per_cpu变量
- 换汽水(华为编程题)
- Linux网络编程多进程模型
- Cookie,DNS,IP
- 类和对象
- hdu 1525 Euclid's Game 博弈论
- CSS中的路径裁剪样式clip-path