第6章 优先队列

第6章 优先队列

6.1 模型

优先队列至少运行有以下两种操作的数据结构 insert 以及deleteMin

6.2 一些简单的实现

1. 我们可以使用一个简单的链表在表头以O1)执行插入操作,并遍历该链表以删除最小元,这又需要O(N)时间,另一种是让链表保持排序状态,这使得插入代价高昂ON),而删除花费低廉O1

2. 另一种是实现是使用二叉查找树 它对这两种操作的平均运行时间都是Olog N)。尽管插入时随机的,而删除不是。反复除去树中的节点会损坏树的平衡

 

6.3 二叉堆

二叉堆的使用对于优先队列的实现相当普通,以至于当堆这个词不加修饰地用在优先队列的上下文中时,一般都是指数据结构的这种实现

堆也有两个性质 结构性 和堆序性

6.3.1 结构性

堆是一棵被完全填满的二叉树,有可能的例外是在底层,底层上的元素从左到右填入

一棵树高为h的完全二叉树有2h 到2h+1 -1 个节点 这也意味着完全二叉树的高是log N 显然它是Olog 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此比较)于是将用时提高到Od logd N

当优先队列太大而不能装入主存时,d-堆也是很有用的

除了不能是实现find外,堆实现的最明显的缺点是  将两个堆合并和一个堆是一个困难的操作 这种操作叫做合并 存在许多实现堆的方法使得一次merge操纵的运算时间是O(log N)

6.6 左式堆

设计一种堆结构像二叉堆那样有效的地支持合并操作(既ON)时间处理一个merge)而且只是用一个数组似乎是很困难的  原因在于 合并似乎需要把一个数组拷贝到另一个数组中去,对于相同大小的堆这将花费时间ON),正因为如此,所有支持有效合并的高级数据结构都需要使用链式数据结构

左式堆 像二叉堆那样也具有结构性和有序性 左式堆和二叉堆都是二叉树,唯一区别是左式堆不是理想平衡的,实际上趋向非常不平衡

6.6.1 左式堆性质

我们把任一节点X的零路径长定义为从X 到一个不具有两个节点的最短路径长,

左式堆性质是  对于堆中的每一个节点,左儿子的零路径长至少与右儿子的零路径长相等(它显然偏重于使树的想左增加深度)

在右路径上右R个节点,那么左式树必然有 2R - 1个节点

 

6.6.2 左式堆操作

对左式堆的基本操作时合并,注意插入只是合并的特殊情景。因为我们可以把插入看成是单节点 的堆与一个大堆的merge

处理验证数据 左引用和右引用所用的空间外,每一个节点还要有一个指示零路径的项

 

根值大的堆与根值小的堆的右子堆合并,若出现不零路径满足情况,将左子堆和右子堆交换    

 

6.7斜堆

斜堆是左手式堆的自调形式,实现起来极其简单,斜堆和左手式堆类似于伸张树和AVL树间的关系,斜堆具有二叉堆的性质,但是不存在堆树的结构限制。不同于左式堆,关于任意节点的零路劲信息不在保留,斜堆的有路劲子啊任何时刻都可以任意长  因此最坏运行时间是O(N) 总的最坏运行时间是OM log N

 

6.8 二项队列

虽然左式堆和斜堆每次操作都是以0log N)时间有效的支持合并,插入和deleteMin但是还有改进的余地,因为我们知道,二叉堆以每次操作花费平常平均时间支持插入,二项队列支持所有的三种操作,每次操纵的最坏情景运行时间为Olog N),而插入操作的平均花费常数时间。

6.8.1 二项队列结构

二项队列与我们已经看到的所有优先队列的实现的区别在于,一个二项队列不是一个堆序的树,而是堆序树的集合,称为森林,每一颗堆序树都是由约束的形式,叫做二项树。每一个高度上至多存在一棵二项树。高度为0的二项树是一棵单节点的 树

 

高度为K的二项树恰好有2k个节点,而深度d处的节点数是二项系数(kd

如果我们把堆序施加到二项树上并允许任意高度上最多一棵,那么久能够用二项树的集合表示任意大小的优先队列。

6.8.2 二项队列操作

此时,最小元可以通过搜索所有书的根来找出,由于最多有log N课不同的树,因此找到最小元的时间为 Olog N)另外,如果我们记住当最小元在其他操作期间变化时更新它,那么也可以保留最小元的信息并以O1)时间执行操作

 

合并两个二项队列在概念上是一个容易的事。(让大的根成为小的根的子树)

由于几乎使用任意合理的实现合并两棵树均花费常数时间,而总的存在O(log N)棵二项树,因此合并最多花费O(log N)时间

 

6.8.3 二项队列的实现

deleteMin操作需要快速找出根的所有子树的能力,因此,需要一般树的标准表示方法:每个节点的儿子都在一个链表中,而且每个节点都有一个对它的第一个儿子(如果有)的引用,该操作还要要求各个儿子按照他们的子树的大小排序,我们要需要保证合并两棵树容易,当两棵树被合并时,其中的一棵树作为儿子被加到另一棵树上。由于这颗新树是一颗最大的子树,因此,以大小递减的方式保持这些子树的意义

 

总而言之,二项树的每一个节点将包含数据,第一儿子以及右兄弟。二项树中各个儿子以降秩次序排列

 

 

小结

标准的二叉堆实现具有简单和快捷的优点,它不需要链,只需要常量附加空间。具有效地支持优先队列的操作。

我们考虑了附加的merge操作,开发了三种实现方法,每种都有其独到之处,左式堆是递归威力的完美例子,斜堆则代表缺少平衡原则的一种重要的数据结构,它的分析时有趣的

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值