B 树是一种被设计成专门存储在磁盘上的平衡查找树。因为磁盘的速度远远慢于内存,所以 B树被设计成尽量减少磁盘访问的次数(引入多叉,降低树的高度),知道了这一点之后就会很清楚明白 B 树的变形 B+树了,B+树通过将数据存储在叶子结点从而增大了一个结点所包含的信息进而更加的减少了磁盘的访问次数。
可合并堆:这种堆支持 Insert,Mininum,Extract-Min,union,delete,decrease-key操作。
二项堆能够在 O(lgn)的最坏情况时间内支持以上的各种操作。当必须支持 union操作时,二项堆优越于二叉堆,因为后者在最坏情况下,合并两个二叉堆要花 O(n)的时间。
斐波那契堆对于以上各种除了 extract-min,delete 的操作外都只需要 O(1)的实际时间,而 extract-min,delete 也只需要 O(lgn)的平摊时间。它的重要优点在于decrease-key 也只需要O(1)的平摊时间。
注意斐波那契堆的一些操作都只是平摊时间,并非最坏情况时间。现代的快速图算法中,很多是使用斐波那契堆作为其核心数据结构的。
不相交集合(查并集):通过用一棵简单的有根树来表示每个集合,就可以得到惊人的快速操作
第十八章 B树
1. 在大多数系统中,B 树算法的运行时间主要由它所执行的 disk-read和 disk-write 操作的次数所决定,因而应该有效地使用这两种操作,即让它们读取更多的信息更少的次数。由于这个原因,在 B 树中,一个结点的大小通常相当于一个完整的磁盘页。因此,一个 B树结点可以拥有的子女数就由磁盘页的大小所决定。
2. 一个结点的总大小不能大于磁盘中一个页的大小,否则在一个结点内操作时还要来回访问内存,反而会拖慢效率。
3. B树定义,所有操作为 为 O(t logtn)
每个叶子节点具有相同的深度
节点关键字为 [ t-1 ,2t-1] t为最小分支数
节点内关键字是有序的
4. B树的查找类二叉树
5. 插入时,分裂是B树升高的唯一途径。:边下行边分裂,当沿着树往下查找新关键字时所属位置时,就分裂沿途遇到的每个满结点(包含叶结点本身,节点数为2t-1, 从第t个关键字处分裂为两个t-1,将第t提升到父节点中)。因此每当要分裂一个满结点时,就能确保它的双亲不是满的。这种方法只需要一趟就可以完成,极大的减少磁盘的读取次数,而这正是B 树所设计追求的。
6。删除时(人工,不是程序):
如果删除关键字k在分支节点中,先使用k的前缀和后缀(较多的那个叶子节点x)交换k
如果 x.num>t-1 直接删除
如果 x.num=t-1, 且x相邻兄弟节点y有 >t-1的,则y 上移到parent,从parent下移到x
如果x.num=t-1,且相邻兄弟节点 y都是 t-1 ,则 parent下来与 x, y合并
第十九章 二项堆
在O(lgn)时间内完成 insert minimum extract-min union delete和decrease-key 的操作
1. 可合并堆就是普通的堆+支持decrease-key,union操作,但是应该高效的实现union操作是可合并堆最关键的部分。
2. UNION操作中,普通堆是O(n),二项堆是O(lgn),斐波那契堆是O(1)
3. 一个二项堆是由一组二项树所构成的。
4. 二项树是一种递归的定义:
1. 二项树B[0]仅仅包含一个节点
2. B[k]是由两棵 B[k-1]二项树组成,其中一颗树是另外一颗树的左子树。
5. 显然二项树具有如下的性质:
a) 对于树 B[k]该树含有2^k个节点;
b) 树的高度是 k;
c) 在深度为 i中含有Ci^k节点,其中 i = 0, 1,2 ... , k;
6. 二项堆有如下性质
1. 每个二项树有最小堆性质
2. 有 0或者1个二项树具有度数k
7. UNION 的大概思路为:将两个二项堆的根表连接起来组成一个大的二项树的连接,按“度”的单调递增顺序进行排序之后,从左至右来消除具有重复度的二项树。
从小到大找到两个相同度K 的二项树,然后连接成一个K+1 度的二项树,直到链尾时合并完毕。
此时的二项堆就满足对任意度K 至多只有一棵二项树。
两种特殊情况,有三个指针 prev x next
x为具有相同度数三个根的第一个:指针集体向前移动
x为具有相同度数两个根的第一个:将x 与next相连,键值小的作为根
8. 其他操作:
插入:
NEW一个一个结点的二项堆,然后调用 UNION 与原二项堆来进行合并
抽取最小关键字的结点:
从原二项堆的根键上取下最小关键字结点的二项树,独立出来;
将独立出来的二项树的根结点去掉(这就是整个二项堆的最小关键字),然后将它的子结点逆转并组合成为一个不含最小值的二项堆;
最后将这个新的二项堆与原来剩下的二项堆进行合并操作。
减少关键字:
将要减小的关键字与它的父结点进行比较,如果需要就进行交换操作直到根链。使得该关键字在堆中“冒泡上升”直到正确的位置。
删除一个关键字(调用其它操作实现):
decrease-key(x, -max)
extract-min()
第二十章 斐波那契堆
1. 斐波那契堆的特点:不涉及删除元素的操作有O(1)的平摊时间。 Extract-Min和Delete的数目和其它相比较小时效率更佳。
2. 斐波那契堆在优化加速图算法中有很大的用途。比如用于解决诸如最小生成树、寻找单源最短路径等问题的快速算法都要用到斐波那契堆。
3. 总结:斐波那契堆之所以高效,就是因为它松散了,将很多二项堆要维护的工作都推迟到了 extract-min 和 delete 操作之中去了。而它之所以可以这样进行偷懒,就是因为它的限制非常的少,并没有像二项堆一样的要求子树全部必须是二项树,而且根表上的任意度数的子树至多只能有一个。没有了这些限制,斐波那契堆就可以实现的非常的松散以至少将日常工作的维护时间都平摊给 extract-min和 delete操作。
但是,正如斐波那契堆现在更多的应用于理论中,实践中使用的并不多的现象所映射的,在大部分的应用中斐波那契堆并不能带来大幅度的效率的提升。因为它并不是减少了要做的工作,只是将很多要做的工作都进行了推迟,可能出现这样的情况:
a) 合并操作 O(lgn)=>O(1)
b) 插入操作 O(lgn)=>O(1)
c) 减少权值操作 O(lgn)=>O(1)
d) 但是 extract-min操作O(lgn)=>O(3*lgn)≈O(lgn)
这使得在理论分析时,它的所有操作的渐近时间都至少不变坏,但是在实际使用中却没有任何的改善。
当然这是最坏的情况了,一般对于规模足够大的输入,斐波那契堆是能够很大改善算法的性能的,尤其是对于一些extract-min和delete 操作少的情况。比如斐波那契堆的使用能加快 Prime 和 Djikstra 算法的执行速度。
第二十一章 不相交集合的数据结构
1. 不相交指没有共同元素,两个重要的操作:查(找出给定元素所属集合)、并集(合并两个集合)。应用场景:无向图中联通子图的个数。
2. 常用的两种表示方法:一种为链表的实现;另一种是更有效的树的表示法。
链表表示:
双向链表;每个节点指向头节点;使用头节点代表集合。
实践时间复杂度是线性,理论上大于线性
合并:每个链表存储长度,将较短的合并到较长的链表上,需要修改指向头节点的指针
森林表示:
每个节点都有指向父的指针,根指向自己;使用根节点代表集合。
渐进时间最快的数据结构,为线性
按秩(树高度的上限,根中存储)合并:秩小的parent指向秩大的树根,如果秩相同,随便指向,并让秩+1
路径压缩: 1)从下往上找到节点的根。 2)从上往下修改该路径上节点的父节点直接指向根
其他高级数据结构
1. 动态图数据结构:在插入和删除边或结点时,还支持各种查询
2. 聚合树:限制全域为整数时,允许更快的二叉查找树支持的操作
3. 持久数据结构:支持在过去版本的数据结构上做查询
4. 动态树:用于渐进最快的网络流算法
5. 伸展树:应用是简化动态树