优先队列
Posted xxMYxx
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了优先队列相关的知识,希望对你有一定的参考价值。
第6章 优先队列
6.1 模型
优先队列至少运行有以下两种操作的数据结构 insert 以及deleteMin
6.2 一些简单的实现
1. 我们可以使用一个简单的链表在表头以O(1)执行插入操作,并遍历该链表以删除最小元,这又需要O(N)时间,另一种是让链表保持排序状态,这使得插入代价高昂O(N),而删除花费低廉O(1)
2. 另一种是实现是使用二叉查找树 它对这两种操作的平均运行时间都是O(log N)。尽管插入时随机的,而删除不是。反复除去树中的节点会损坏树的平衡
6.3 二叉堆
二叉堆的使用对于优先队列的实现相当普通,以至于当堆这个词不加修饰地用在优先队列的上下文中时,一般都是指数据结构的这种实现
堆也有两个性质 结构性 和堆序性
6.3.1 结构性
堆是一棵被完全填满的二叉树,有可能的例外是在底层,底层上的元素从左到右填入
一棵树高为h的完全二叉树有2h 到2h+1 -1 个节点 这也意味着完全二叉树的高是log N 显然它是O(log N)
对于数组上任意的 i 上的元素,其左儿子在位置2i上,右儿子在做儿子后的单元(2i + 1)上 它的父亲则在(i/2)上
这种方法的最大问题在于 最大的堆的大小需要事先估计,但一般都不成问题
一个堆的结构将由一个(Comparable对象的)数组和一个代表当前堆的大小的整数组成
6.3.2 堆序性质
让操作快速执行的性质是堆序性质。由于我们想要快速找出最小元,因此最小元应该在根上
6.3.3 基本堆操作
无论是概念上还是实际上考虑,执行这两个所要求的操作都是容易的。所有的工作都需要保证始终保持堆序性质
insert
采用的策略叫做上滤,在下一个可用的位置创建一个空穴,然后比较父类是否比他小,大则上滤
deeteMin
采用的策略叫做下滤 删除根元素,并填充空穴,然后将最堆中最后一个元素放入空穴中,将空穴和子元素比较
我们必须保证节点只有一个儿子的前提下程序正确执行
6.4 优先队列的应用
1.选择问题
2.事件模拟
6.5 d-堆
二叉堆是如此的简单,以至于他们几乎总是用在需要优先队列的时候,d-堆是二叉堆的简单推广,它就像一个二叉堆,只是所有节点都有d个儿子(因此二叉堆 又是 2堆)
d-堆要比二叉堆浅的多,它将insert操作时间的运行时间改进为O(logd N) 然而,对于大的d deleteMin操作费时的多。以为d个儿子中必须找出一个最小者(d-1此比较)于是将用时提高到O(d logd N)
当优先队列太大而不能装入主存时,d-堆也是很有用的
除了不能是实现find外,堆实现的最明显的缺点是 将两个堆合并和一个堆是一个困难的操作 这种操作叫做合并 存在许多实现堆的方法使得一次merge操纵的运算时间是O(log N)
6.6 左式堆
设计一种堆结构像二叉堆那样有效的地支持合并操作(既O(N)时间处理一个merge)而且只是用一个数组似乎是很困难的 原因在于 合并似乎需要把一个数组拷贝到另一个数组中去,对于相同大小的堆这将花费时间O(N),正因为如此,所有支持有效合并的高级数据结构都需要使用链式数据结构
左式堆 像二叉堆那样也具有结构性和有序性 左式堆和二叉堆都是二叉树,唯一区别是左式堆不是理想平衡的,实际上趋向非常不平衡
6.6.1 左式堆性质
我们把任一节点X的零路径长定义为从X 到一个不具有两个节点的最短路径长,
左式堆性质是 对于堆中的每一个节点,左儿子的零路径长至少与右儿子的零路径长相等(它显然偏重于使树的想左增加深度)
在右路径上右R个节点,那么左式树必然有 2R - 1个节点
6.6.2 左式堆操作
对左式堆的基本操作时合并,注意插入只是合并的特殊情景。因为我们可以把插入看成是单节点 的堆与一个大堆的merge
处理验证数据 左引用和右引用所用的空间外,每一个节点还要有一个指示零路径的项
根值大的堆与根值小的堆的右子堆合并,若出现不零路径满足情况,将左子堆和右子堆交换
6.7斜堆
斜堆是左手式堆的自调形式,实现起来极其简单,斜堆和左手式堆类似于伸张树和AVL树间的关系,斜堆具有二叉堆的性质,但是不存在堆树的结构限制。不同于左式堆,关于任意节点的零路劲信息不在保留,斜堆的有路劲子啊任何时刻都可以任意长 因此最坏运行时间是O(N) 总的最坏运行时间是O(M log N)
6.8 二项队列
虽然左式堆和斜堆每次操作都是以0(log N)时间有效的支持合并,插入和deleteMin但是还有改进的余地,因为我们知道,二叉堆以每次操作花费平常平均时间支持插入,二项队列支持所有的三种操作,每次操纵的最坏情景运行时间为O(log N),而插入操作的平均花费常数时间。
6.8.1 二项队列结构
二项队列与我们已经看到的所有优先队列的实现的区别在于,一个二项队列不是一个堆序的树,而是堆序树的集合,称为森林,每一颗堆序树都是由约束的形式,叫做二项树。每一个高度上至多存在一棵二项树。高度为0的二项树是一棵单节点的 树
高度为K的二项树恰好有2k个节点,而深度d处的节点数是二项系数(kd)
如果我们把堆序施加到二项树上并允许任意高度上最多一棵,那么久能够用二项树的集合表示任意大小的优先队列。
6.8.2 二项队列操作
此时,最小元可以通过搜索所有书的根来找出,由于最多有log N课不同的树,因此找到最小元的时间为 O(log N)另外,如果我们记住当最小元在其他操作期间变化时更新它,那么也可以保留最小元的信息并以O(1)时间执行操作
合并两个二项队列在概念上是一个容易的事。(让大的根成为小的根的子树)
由于几乎使用任意合理的实现合并两棵树均花费常数时间,而总的存在O(log N)棵二项树,因此合并最多花费O(log N)时间
6.8.3 二项队列的实现
deleteMin操作需要快速找出根的所有子树的能力,因此,需要一般树的标准表示方法:每个节点的儿子都在一个链表中,而且每个节点都有一个对它的第一个儿子(如果有)的引用,该操作还要要求各个儿子按照他们的子树的大小排序,我们要需要保证合并两棵树容易,当两棵树被合并时,其中的一棵树作为儿子被加到另一棵树上。由于这颗新树是一颗最大的子树,因此,以大小递减的方式保持这些子树的意义
总而言之,二项树的每一个节点将包含数据,第一儿子以及右兄弟。二项树中各个儿子以降秩次序排列
小结
标准的二叉堆实现具有简单和快捷的优点,它不需要链,只需要常量附加空间。具有效地支持优先队列的操作。
我们考虑了附加的merge操作,开发了三种实现方法,每种都有其独到之处,左式堆是递归威力的完美例子,斜堆则代表缺少平衡原则的一种重要的数据结构,它的分析时有趣的
以上是关于优先队列的主要内容,如果未能解决你的问题,请参考以下文章