您的位置:首页 > 理论基础 > 数据结构算法

算法与数据结构八日谈之六——数据结构专题(uncompleted)

2015-07-05 22:53 561 查看
这是个大工程

我要慢慢写

朱军稍候

UPD:暂时更完了,代码部分再等会吧,估计很长。

1.最·基础数据结构

数组

别问我为什么把这放进来,我就是闲着没事做

可以在栈空间不够用时用手写栈来进行dfs。

队列

bfs所需的基础数据结构,在spfa时也会用到

链表

在插入操作较多时可以使用链表来维护

hash时可将重复元素以链表的形式挂在一张表上

2.基础数据结构

上面那些东西(除了链表)是毫无实现难度的,学过计算机语言的就会使用,下面这些则需要一点点的算法知识

树状数组

代码简洁,常数小。

可以维护单点修改和区间求和/极值

利用两个树状数组可以区间修改和单点求值

利用三个树状数组可以区间修改和区间求值(然而并没有什么卵用)

一般可以在两行解决问题

线段树

思路简单,编程复杂度小

可以维护区间和/极值。支持区间修改和单点修改

权值线段树

线段树的区间端点由原序列的下标变为值域

在数值与n同阶或可以离线操作的情况下各种操作的速度比大多数平衡树要快(跟vEB树差不多)

于是排名就是前缀和,其它的基本操作随便搞搞就可以了

zkw线段树

zkw神的线段树,由顶向下实现,(据说)速度奇快无比。具体的参见其论文《统计的力量》

(普通的)堆

堆可以动态维护集合里的极值。

支持将极值从集合中弹出,向集合中加入一个数,询问极值。

(普通的堆)插入和删除都是O(lgn)的

可以在O(n)的时间内建一个大小为n的堆

堆排序即利用堆按大小顺序依次取出每个元素,时间复杂度是O(nlgn)的

3.进阶数据结构

平衡树

平衡树维护可以判断元素大小关系的集合。

伸展树Splay

伸展树的核心操作是伸展(废话)

每次访问节点,则需要将其旋转到根的位置,这个操作被称为伸展

splay能称作平衡的核心是在伸展中的某种trick

当祖孙三代方向一致时,先转父亲,再转儿子,否则转儿子转两次。

由势能分析可以得到伸展树的均摊复杂度是Θ(lgn)的(别问我,我不知道咋分析)

伸展树最重要的操作是提取区间,这是它能在oi界中生存的最大的依仗(之一),每次将待提取的区间的左端点左边那个点伸展到根节点,再将区间右端点右边那个点伸展到根节点右儿子处,此时根节点右儿子的左儿子就是所求区间。

splay可以解决很多需要线段树解决的问题。

非常实用的数据结构,不学不算搞过oi

树堆Treap

可以证明(你™就是不会证瞎扯个啥),随机插入的二叉排序树的深度是O(lgn)的。于是我们可以强制使插入的数据呈随机的顺序插入。

Treap=Tree+heap,我们给每个节点增加一个域,并将该域的值赋为一个随机数,姑且叫做w[n]。对于原数据,treap为一棵二叉检索树;对于w[i]treap是一个大根/小根堆。

treap的实现很简单,与普通的二叉检索树没有很大的区别。

如果在插入后子树的w域不满足堆的性质,则将其旋转至原节点上方。

同理,删除时也要考虑维护堆的性质。

treap的编程复杂度较低,非常适合在考场上写,而且效率是相当不错的。

节点大小平衡树SBT

喜闻乐见的傻逼树。

不需要额外的域来保存信息。

论文上说的很神,但可以用人字形的数据卡掉。。。

实际效率比上面那俩要高,但它的maintain操作太淡腾并不好写。

(好吧我不会写这玩意,我在瞎哔哔)

朝鲜树

湖北的上古神犇albus随手yy出的数据结构,为vfk领悟下面这玩意做了铺垫(叫这个名字只因为金正中的名字与朝鲜前领导人二儿子重合)

替罪羊树Scapegoat Tree

神一样的数据结构(之一),在实践中是最快的平衡树。

通过设置参数来平衡插入与查询的常数复杂度。

当插入时存在一个节点的左右儿子大小的最大值大于α乘该节点大小,则重建以该节点为根的子树。

通过复杂度分析可以知道(我不知道),替罪羊树的“各种”操作的复杂度是均摊Θ(lgn)的。

而且其不需要旋转的特性为其套上其它数据结构提供了可能。

块状链表

万能数据结构

splay能干的它都能干。

splay不能干的它也能干。

它啥都能干。

基于分块思想的数据结构,在处理询问时相当暴力,这也为其提供了很大的灵活性。

所有操作几乎都是暴力。套上平衡树后还能顺便解决区间k大值问题。

说起来简单,但实现却真是哔了狗了,参见orzjry这道题。

4.高端数据结构(雾)

可持久化数据结构

可持久化(函数式)线段树,也称主席树

在完成普通线段树能完成的任务时,还能支持询问历史版本。

将权值线段树的思想应用到主席树上,可以用主席树解决静态的区间k大值问题。

函数式的思想是,只定义,不修改,我们每次插入一个值时,都从根开始建一(/半)棵新的主席树,因为有一半是相同的,所以可以利用原来的线段树的信息从而减少空间消耗。

可持久化字典树

利用可持久化字典树可以解决区间最大异或和问题。

仍然是利用函数式的原理,每次插入时都新建节点。每次询问从高位向低位贪心,实现与主席树类似。

可持久化平衡树

平衡树的旋转操作很可怕,修改了一大堆的东西,不适合可持久化。

我们对treap进行一些修改,引入split和merge两个操作,分别是把一个treap以某个数为界分为俩treap,还有把两个值域不相交的treap合并。

这样的话插入或删除都只需要先split再merge即可,而这两个操作不需要旋转,这对可持久化提供了可能。

具体的实现我不会,所以上面这坨也是我瞎哔哔的,所以还是去搜论文吧。

数据结构的嵌套

线段树套线段树,也称二维线段树

最“常见”的数据结构嵌套的情形之一,白书上就提供了具体的实现。

实现虽然复杂,但是原理却相当简单,如果明白了给出足够的时间是可以写出来的。

但二维线段树不支持区间修改。

但二维线段树不支持区间修改。

因为比较重要所以要说两遍。

所以很多时候需要差分或者其它的把区间修改为单点修改。

线段树套线段树套线段树,也称三维线段树

sto VFleaKing orz

树状数组套主席树

在解决有修改操作的区间k大值时,原版的主席树就不能直接上了,因为需要修改O(n)棵主席树,这是无法接受的复杂度。

考虑主席树维护的是前缀和,而树状数组维护的前缀和只需要访问O(lg)个节点,所以可以考虑树状数组套主席树。

每次修改时,修改树状数组对应的O(lgn)棵主席树即可,所以修改的复杂度是O(lg2n),询问类似,复杂度与修改相当。

这是相当优秀的一种待修改区间k大值问题的解法,树状数组极低的编程复杂度决定了它的实用性。

线段树套平衡树

线段树这傻大个,不会旋转,所以可以随便套。

对于每棵线段树,我们对于其中的值建一棵平衡树。

空间O(n2)会炸?

有这种想法的同学再想想线段树到底是啥。

线段树套平衡树可以求区间k大值,区间排名,区间前驱,区间后继,以及修改(二逼平衡树)。

对于区间排名,只需将原区间以线段树的方式分为O(lgn)个区间,然后对于每个区间的平衡树上求出小于其的数的个数之和,再加1就是排名,复杂度是O(lg2n)的。

然后是区间k大,二分答案,直到排名等于k,复杂度是O(lg3n),在时限接受的数据范围内与O(n√lgn)的块链套平衡树差不多,但后者常数太大,效率没法比。

前驱后继与平衡树的操作类似,不再赘述。

替罪羊树套平衡树

继续sto VFleaKing orz

vfk的神题:带插入区间k大值。

有了插入操作,除了块链,前面的树套树变得毫无意义。

插入就需要平衡,平衡需要旋转,一旋转就炸飞了。

我们需要考虑不需要旋转或者旋转次数很少的平衡树。

对,替罪羊树!

对于原区间的我们不再使用线段树,而是改成用替罪羊树维护。

在插入时如果大小不满足替罪羊树的要求,就对于该子区间和那些子区间所对应的平衡树重建。

可以证明,复杂度是均摊Θ(lglgn)的。

因为重建的原因,在处理时要使用垃圾回收,不然空间复杂度不能接受。

k-d树

这玩意我为什么会单独扯出一个小节呢,是有原因的。

k-d树的原理

k-d树的每个节点对应了空间的一部分。

k-d树是基于二分的数据结构,在建树时,轮换进行分割的维,然后取中位数为当前子树的根,再递归进行建树操作。

k-d树的基本应用

k近邻查询

在询问时将可能成为k近邻的点放入堆中,然后在分别访问子树中可能成为答案的点。

空间最近点对

暴力一点,对于每个点,求离它最近的点,然后更新答案。

询问给定点的最近曼哈顿距离

询问给定点的最远曼哈顿距离

询问给定点的最近欧几里得距离

询问给定点的最远欧几里得距离

以上四种询问都需要一个估价函数,估价函数对于一个区域进行估价,并返回估价值。

在查询时左/右子树谁的估价更好就优先访问该子树,当然如果没有当前答案优就可以直接剪枝。

理论上最坏的查询复杂度是O(n√)的

k-d树的进阶应用

维护凸壳

原理也比较简单,在每次插入时去掉那些不可能在凸壳上的点,然后询问与k近邻查询类似。

维护多维空间的区域和

利用k-d树独特的性质,可以用其处理一些用二(多)维线段树处理的问题。

利用替罪羊重建维护k-d树的平衡

在插入时如果都在某个子树上时,k-d树的时间复杂度会退化为O(n)。

而k-d树的结构注定其无法旋转,于是考虑替罪羊树的结构,我们也可用同样地方法来维护。

k-d树的实现技巧

估价函数的写法

要注意k-d树对应的是一个区域,所以我们在维护时顺便维护区域的极值点(例如矩形的两个顶点),然后在估价时以区域可能的最佳取值为估价值。

坐标转换

对于某些询问,询问的区域是一个菱形,我们可以通过适当的操作把询问变换为正方形。就像这样

x′=x+yy′=x−y

然后就完了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  数据结构 平衡树