[Splay的应用]
2015-08-07 14:29
204 查看
广泛应用
引言中提到Splay的应用十分广泛,实际也如此。这里介绍它的一个十分特别的应用:在某些情况下替代块状链表。
替代块状链表对于什么是块状链表,本文不做介绍,有兴趣的读者可以去阅读相关论文(如苏煜的论文《对块状链表的一点研究》)。
块状链表的作用就是维护一个序列。而Splay作为一个树状结构,怎么做到这点呢?我们考虑给序列的每个元素加上一个权值,这个值就是元素在序列中的位置。我们可以发现,这时以这个值为关键字将这个序列插入一棵二叉查找树,树的中序遍历竟然就是这个序列!而无论树的形态如何变化,只要没有破坏二叉查找树的性质,我们可以很容易发现树的中序遍历也不会发生变化。所以,伸展操作不会破坏中序遍历,Splay也就可以用来存储这个序列。这样维护序列的时间复杂度为O(nlogn),在部分题目中完全可以替代块状链表(时间复杂度为O(n*sqrt(n)))。
实际上,我们没有必要加入权值,它除了能够帮助我们理解为什么树状结构可以存储一个序列以外没有任何其它的作用。实现的时候只需要根据序列递归建树即可(每次以序列最中间的元素为树根,左右两部分元素递归建树)。
实际应用来看一个Splay很经典的应用。
题目要求对于一个序列,维护三种操作:删除当前序列第x个元素到第y个元素(例如序列ADBCDCE删除第2~4个元素之后变为ADCE),在当前序列第x个元素后插入一段元素(例如ABCD在第2个元素后插入序列DEC变成ABDECCD),将当前序列的第x个元素到第y个元素逆序(如ACEDCBDF的第3~7个元素逆序后变成ACDBCDEF)。
第一个操作的实现很容易:只需要先把第x-1个元素伸展到根,再把第y+1个元素伸展到x的右子树的根。这样,第x个元素到第y个元素的序列就会作为第y+1个元素的左子树在平衡树里出现,如图4(和“删除大于l小于r的数”操作很像,自己用几个例子画画吧^_^),删除这棵子树即可。
图4伸展后树的形态
第二个操作更加简单:先把第x个元素伸展到根,再把第x+1个元素伸展到x的右子树的根。此时再把需要插入的序列建立一棵子树插入到第x+1个元素的左子树(原先为空)即可。这个操作和第一个操作很像。
那么,对于第三个操作,我们要怎么办呢?
这里,我们要借鉴线段树的一个经典操作——懒标记。
首先,把第x-1个元素伸展到根;然后,将第y+1个元素伸展到根的右孩子。此时,第x个元素到第y个元素组成的序列就出现在第y+1个元素的左子树的位置上。此时,对此子树的根打上一个懒标记(如果已经存在一个懒标记则撤消),说明子树表示的序列需要被翻转,而不必立即进行翻转。当之后访问到这个结点的时候,只需要将懒标记“下放”到两个孩子并交换两个孩子的位置即可。这样做的正确性毋庸置疑,而时间复杂度显然不会增加一个数量级——懒标记的下放只不过在旋转操作中顺便进行罢了。
参考文献及附件
《伸展树的基本操作与应用》,杨思雨,2006
《伸展树操作详解》,马朔,2006
《对块状链表的一点研究》,苏煜,2007
superbt,作者未知,时间未知
《生日快乐》,NOI,2006
摘自--《神奇的Splay-TheMagical Splay-product of sqybi》
引言中提到Splay的应用十分广泛,实际也如此。这里介绍它的一个十分特别的应用:在某些情况下替代块状链表。
替代块状链表对于什么是块状链表,本文不做介绍,有兴趣的读者可以去阅读相关论文(如苏煜的论文《对块状链表的一点研究》)。
块状链表的作用就是维护一个序列。而Splay作为一个树状结构,怎么做到这点呢?我们考虑给序列的每个元素加上一个权值,这个值就是元素在序列中的位置。我们可以发现,这时以这个值为关键字将这个序列插入一棵二叉查找树,树的中序遍历竟然就是这个序列!而无论树的形态如何变化,只要没有破坏二叉查找树的性质,我们可以很容易发现树的中序遍历也不会发生变化。所以,伸展操作不会破坏中序遍历,Splay也就可以用来存储这个序列。这样维护序列的时间复杂度为O(nlogn),在部分题目中完全可以替代块状链表(时间复杂度为O(n*sqrt(n)))。
实际上,我们没有必要加入权值,它除了能够帮助我们理解为什么树状结构可以存储一个序列以外没有任何其它的作用。实现的时候只需要根据序列递归建树即可(每次以序列最中间的元素为树根,左右两部分元素递归建树)。
实际应用来看一个Splay很经典的应用。
题目要求对于一个序列,维护三种操作:删除当前序列第x个元素到第y个元素(例如序列ADBCDCE删除第2~4个元素之后变为ADCE),在当前序列第x个元素后插入一段元素(例如ABCD在第2个元素后插入序列DEC变成ABDECCD),将当前序列的第x个元素到第y个元素逆序(如ACEDCBDF的第3~7个元素逆序后变成ACDBCDEF)。
第一个操作的实现很容易:只需要先把第x-1个元素伸展到根,再把第y+1个元素伸展到x的右子树的根。这样,第x个元素到第y个元素的序列就会作为第y+1个元素的左子树在平衡树里出现,如图4(和“删除大于l小于r的数”操作很像,自己用几个例子画画吧^_^),删除这棵子树即可。
图4伸展后树的形态
第二个操作更加简单:先把第x个元素伸展到根,再把第x+1个元素伸展到x的右子树的根。此时再把需要插入的序列建立一棵子树插入到第x+1个元素的左子树(原先为空)即可。这个操作和第一个操作很像。
那么,对于第三个操作,我们要怎么办呢?
这里,我们要借鉴线段树的一个经典操作——懒标记。
首先,把第x-1个元素伸展到根;然后,将第y+1个元素伸展到根的右孩子。此时,第x个元素到第y个元素组成的序列就出现在第y+1个元素的左子树的位置上。此时,对此子树的根打上一个懒标记(如果已经存在一个懒标记则撤消),说明子树表示的序列需要被翻转,而不必立即进行翻转。当之后访问到这个结点的时候,只需要将懒标记“下放”到两个孩子并交换两个孩子的位置即可。这样做的正确性毋庸置疑,而时间复杂度显然不会增加一个数量级——懒标记的下放只不过在旋转操作中顺便进行罢了。
参考文献及附件
《伸展树的基本操作与应用》,杨思雨,2006
《伸展树操作详解》,马朔,2006
《对块状链表的一点研究》,苏煜,2007
superbt,作者未知,时间未知
《生日快乐》,NOI,2006
摘自--《神奇的Splay-TheMagical Splay-product of sqybi》
相关文章推荐
- Oracle to_date()函数的用法
- 关于一个时间转换小功能开发
- 截取文本里特定连续字段并去掉多余空格
- Object源码研究1——整体研究
- jwhois
- HDU 4508(湫湫系列故事——减肥记I)基础完全背包
- Data collection is easy. Decision making is hard.
- string类详解
- html中<base>标签的使用,和简介
- 算法题:二叉树的构造
- 碰撞运动js
- 苹果App转移图文详解-Transfer App
- JavaScript中callee,caller,argument的理解
- C++宏定义详解
- maven 导入本地jar包
- Java系列笔记(5) - 线程
- Supervisor 安装与配置指南
- Cocos2d-x学习笔记(三)—— 坐标系
- 中国剩余定理模板poj1006
- C++类的继承