http://blog.csdn.net/bin314/article/details/7899934
部分内容参考自 论文 苏煜《对块状链表的一点研究》
1. 数组和链表对比:
操作 | 数组 | 链表 |
---|---|---|
存储结构 | 地址连续的存储单元,物理位置相邻 | 地址不连续,物理位置不相邻 |
定位 | O(1) | O(N) |
添加 | O(N) | O(1) |
删除 | O(N) | O(1) |
数组有很好的定位功能,一般对应于固定的长度,不适合添加/删除等操作,当数组有序时,可二分查找某值,效率很好,O(logN)。
链表添加删除效率极高,很适合这类操作。但定位效率很低。
2.块状链表
块状链表是对数组和链表的折中,集两者之长,在定位,添加删除的效率上都有大幅提升。
整体上使用链表,链表节点是一个大小适当的数组。
如下图:
3.基本操作
①:定位
从链表头开始往后扫,每个节点记录本节点合法数据的长度,最终会定位为某一个块及块内偏移。
②:分裂
将指定的块在指定的位置分裂成2个块。
③:合并
本块合并掉之后的那一块,前提是2块的有效数据长度之和 <= N (array的长度)
在定位,插入,删除的时候对本块进行合并将会减少块元素过少,否则有可能退化成普通的链表。
④:插入
在指定位置分裂,然后在本块后面插入若干个块,示意图如下:
⑤:删除
在指定的位置分裂,删除本块之后的若干块,示意图如下:
4.效率分析:
设数组大小为x,数据总数为N,则理想状况下分块树为N/x,则定位的时间复杂度为O(N/x),插入删除的时间复杂度为O(x)
令N/x = x , x = sqrt(N),即每次操作的时间复杂度大致为O(sqrt(N))
和平衡树O(logN)等相比还是有较大差距,但是其附加空间很少,仅为O(sqrt(N)),平衡树的附加空间为O(N)。
5.几道例题:
①:NOI2003 editor(经典)
【题目大意】
一些定义:
文本:由0个或多个ASCII码在闭区间[32, 126]内的字符(即空格和可见字符)构成的序列。
光标:在一段文本中用于指示位置的标记,可以位于文本首部,文本尾部或文本的某两个字符之间。
文本编辑器:为一个包含一段文本和该文本中的一个光标的,并可以对其进行如下六条操作的程序。如果这段文本为空,我们就说这个文本编辑器是空的。
操作名称
输入文件中的格式
功能
MOVE(k)
Move k
将光标移动到第k个字符之后,如果k=0,将光标移到文本开头
INSERT(n, s)
Insert n¿
S
在光标处插入长度为n的字符串s,光标位置不变,n ³ 1
DELETE(n)
Delete n
删除光标后的n个字符,光标位置不变,n ³ 1
GET(n)
Get n
输出光标后的n个字符,光标位置不变,n ³ 1
PREV()
Prev
光标前移一个字符
NEXT()
Next
光标后移一个字符
比如一个空的文本编辑器依次执行操作INSERT(13, “Balanced tree”),MOVE(2),DELETE(5),NEXT(),INSERT(7, “ editor”),MOVE(0),GET(16)后,会输出“Bad editor tree”。
你的任务是:
建立一个空的文本编辑器。
从输入文件中读入一些操作并执行。
对所有执行过的GET操作,将指定的内容写入输出文件。
ps:这些操作仅仅是定位,添加,删除,采用块状链表可以轻松搞定,splay等各种也可以。
有兴趣的可以在下oj上测试下自己的代码:
②:反转序列
【题目大意】
一个长度为 n 的整数序列初始时从左到右为1,2,3,……,n,现在对这个序列进行 m 次操作,每次把 p 到 q 的子序列反转 求最后的序列ps:操作很简单,每次对一个区间进行反转,求若干次反转之后的序列。
这里需要在每个节点维护一个域rev,rev=true代表本区间需要反转,false表示不需要反转
在区间合并、分裂以及最后的输出上需要维护rev,操作很简单,反转该区间的值即可。
在反转若干的区间的时候问题等价于将链表反转。
有兴趣的可以在下oj上测试下自己的代码:
http://cstest.scu.edu.cn/soj/problem.action?id=3035
ps:用一个数组维护每一块的长度,查找时可以二分,添加、删除时采用顺序操作。
删除是二分定位可以跳过中间的直接删掉所有待删除块。
因为移位效率低,用平衡树来维护就舍本逐末了,目测效率也没多大改进。
不过对于反转序列来说,因为不涉及添加删除,效果还是可观的。