优先队列

在多用户环境中,操作系统调度程序必须决定在若干进程中运行哪个进程。一般一个进程只被允许运行一个固定的时间片。

一、模型

1、优先队列是允许至少下列两种操作的数据结构:insert以及deleteMin(删除最小者,找出、返回并删除优先队列中最小的元素)。insert操作等价于enqueue(入队),deleteMin是队列运算dequeue(出队)在优先队列中的等价操作。

二、一些简单的实现

1、实现优先队列的方法

(1)使用一个简单链表在表头以O(1)执行插入操作,并遍历该链表以删除最小元,这又需要O(N)时间。

(2)始终让链表保持排序状态,这使得插入代价高昂(O(N)而deleteMin花费低廉(O(1)))。

(3)使用二叉查找树。

三、二叉堆(堆)

堆的两个性质:结构性和堆序性。对堆的一次操作可能破坏这两个性质中的一个。因此,堆的操作必须到堆的左右性质都被满足时才能终止。

1、结构性质

(1)堆是一棵被完全填满的二叉树,有可能的例外是在底层,底层上的元素从左到右填入。这样的树称为完全二叉树。

(2)一棵高为h的完全二叉树有2k到2k+1-1个节点。这意味着完全二叉树的高是log N。

(3)完全二叉树可以用一个数组表示而不需要使用链。

(4)一个堆结构将由一个(Comparable对象的)数组和一个代表当前堆的大小的整数组成。

2、堆序性质

(1)让操作快速执行的性质是堆序性质。

(2)在一个堆中,对于每一个节点X,X的父亲中的关键字小于(或等于)X中的关键字,根节点除外(它没有父亲)。

(3)根据堆序性质,最小元总可以在根处找到,因此我们以常数时间得到附加操作findMin。

3、基本的堆操作

(1)insert

为将一个元素X插入到堆中,我们在下一个可用位置创建一个空穴,否则该堆将不是完全树。如果X可以放在该空穴中而不破坏堆的序,那么插入完成。否则,我们把空穴的父亲点上的元素移入该空穴中,这样空穴就朝着根的方向上冒一步。继续该过程直到X能够被放入空穴中为止。

上滤:新元素在堆中上滤直到找出正确的位置。

(2)deleteMin

当删除一个最小元时,要在根节点建立一个空穴。由于现在堆少了一个元素,因此堆中最后一个元素X必须移动到该堆的某个地方。如果X可以被放到空穴中,那么deleteMin完成。不过这一般不太可能,因此我们将空穴的两个儿子中较小者移入空穴,这样就把穴空间下推了一层。重复该步骤直到X可以被放入空穴中。因此,我们的做法是将X置入沿着从根开始包含最小儿子的一条路径上的一个正确的位置。

(3)当堆中存在偶数个元素的时候,在每个下滤开始处,可将其值大于堆中任何元素的标记放到堆的终端后面的位置上。

4、其他堆操作

(1)一个堆所蕴含的序信息很少,若不对整个堆进行线性搜索,是没有办法找出任何特定的关键字的。

(2)decreaseKey(降低关键字的值)

decreaseKey(p,∆)操作降低在位置p处的项的值,降值的幅度为正的量∆。由于这可能破坏堆序性质,因此必须通过上滤对堆进行调整。

(3)increaseKey(增加关键字的值)

increaseKey(p,∆)操作增加在位置p处的项的值,增值的幅度为正的量的∆。这可以使用下滤来完成。

(4)delete(删除)

delete(p)操作删除堆中位置p上的节点。该操作通过首先执行decreaseKey(p,∞)然后再去执行deleteMin()来完成。当一个进程被用户终止(而不是正常终止)时,它必须从优先队列中除去。

(5)buildHeap(构建堆)

有时二叉堆是由一些项的初始集合构造而得。这种构造方法以N项作为输入,并把它们放到一个堆中。

一般的算法是将N项以任意顺序放入树中,保持结构特性。

每条虚线对应两次比较:一次找出较小儿子的节点,另一个是较小的儿子与该节点的比较。为了确定buildHeap的运行时间的界,我们必须去确定虚线的条数的界。这可以通过计算堆中所有节点的高度的和来的到,它是虚线的最大条数。该和为O(N)。

四、d-堆

1、d-堆是二叉堆的简单推广,所有的节点都有d个儿子。

2、d-堆要比二叉堆浅的多,它将insert操作的运行时间改进为O(logdN),对于大的d,deleteMin操作费时的多。如果d是常数,运行时间为O(log N)。

五、左式堆

左式堆具有结构性和有序性,左式堆也是二叉树,左式堆不是理想平衡的,而实际上趋于非常不平衡。

1、左式堆性质

(1)把任一节点X的零路径长npl(X)定义为从X到一个不具有两个儿子的节点的最短路径的长。因此,具有0个或一个儿子的节点的npl为0,而npl(null)=-1)。任一节点的零路径长比它的各个儿子节点的零路径长的最小值大1。

(2)左式堆性质:对于堆中的每一个节点X,左儿子的零路径长至少与右儿子的零路径长相等。

(3)因为左式堆趋向于加深左路径,所以右路径应该短。事实上,沿左式堆右侧的右路径确实是该堆中最短的路径。否则,就会存在过某个节点X的一条路径通过它的左儿子,此时X就破坏了左式堆的性质。

(4)在右路径上有r个节点的左式树必然至少有2r-1个节点。

(5)N个节点的左式树有一条右路径最多含有log(N+1)个节点。

(6)对左式堆的操作的一般思路是将所有的工作放到右路径上进行,它保证树深度短。

2、左式堆操作

(1)堆左式堆的基本操作是合并。插入只是合并的特殊情形。

(2)如果两个堆中有一个堆是空的,那么可以返回另一个堆。否则,合并这两个堆,比较它们的根。首先,递归的将具有大的根值的堆与具有小的根值的堆的右子堆合并。

(3)执行合并的时间与诸右路径的长的和成正比,因为在递归调用期间对每一个被访问的节点花费的是常数工作量。合并两个左式堆的时间界为O(log N)。

 六、斜堆

1、斜堆是左式堆deep自调节形式。斜堆是具有堆序的二叉树,但不存在对树的结构限制。斜堆的右路径在任何时刻都可以任意长,因此,所有操作的最坏情形运行时间均为O(N)。

2、斜堆的基本操作是合并操作。斜堆交换是无条件的,除那些右路径上所有节点的最大者不交换它的左右儿子例外外,都要进行交换。

3、斜堆不需要附加的空间保留路径长以及不需要测试以确定何时交换儿子。

七、二项队列

1、二项队列结构

(1)一个二项队列不是一棵堆序的树,而是堆序的树的集合,称为森林。每一棵堆序树都是有约束的形式,叫做二项树。每一个高度上至多存在一棵二项树。高度为0的二项树是一棵单节点树,高度为k的二项树Bk通过将一棵二项树Bk-1附接到另一棵二项树Bk-1的根上而构成。高度为k的二项树有2k个节点。

2、二项队列操作

(1)合并操作基本上是通过将两个队列加到一起来完成的。

3、二项队列的实现

(1)deleteMin操作需要快速找出根的所有子树的能力,因此,需要一般树的标准表示方法:每个节点的儿子都在一个链表中,而且每个节点都有一个对它的第一个儿子(如果有的话)的引用。该操作还要求各个儿子按照它们的子树的大小顺序。

(2)当两棵树被合并时,其中一棵树作为儿子被加到另一棵树上。由于这棵树将是最大的子树,因此,以大小递减的方式保持这些子树是有意义的。只有这时我们才能够有效的合并两棵二项树从而合并两个二项队列。二项队列将是二项树的数组。

(3)二项树的每一个节点将包含数据、第一个儿子以及右兄弟。二项树中的各个儿子以降秩次序排列。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值