数据结构算法与应用第九章-优先队列

第六章讲述了FIFO队列,第八章讲述了二叉树,在本章讲述优先队列。内容包括优先队列的线性表描述(公式描述线性表和链表)、堆、左高树、堆排序问题、机器调度问题和霍夫曼编码问题。

 

优先队列定义:优先队列是0个或者多个元素的集合,每个元素都有一个优先权,优先队列的操作要基于优先权进行。

优先队列的分类:最小优先队列和最大优先队列。

最大优先队列的操作:

1)  插入,根据元素优先权插入元素到队列。

2)  删除,从优先队列中删除具有最大优先权的元素。

3)  查询,在优先队列中查询具有最大优先权的元素。

 

对比优先队列和FIFO队列的操作,就可以改写FIFO队列从而得到优先队列,只需要在部分操作时进行一次最大优先级的查询即可。由于加入查询,从FIFO改写得到的优先队列性能较FIFO队列差了许多。

优先队列描述方法

插入操作

查询操作

删除操作

无序公式化描述线性表

O(1)

O(n)

O(n)

有序公式化描述线性表

O(n)

O(1)

O(1)

无序链表

O(1)

O(n)

O(n)

有序链表

O(n)

O(1)

O(1)

以上是线性表描述的优先队列各个操作的性能,下面讲述另外一种优先队列的描述方法。

 

最大树(最小树):每个节点的值都大于或者等于其每个子节点的值的树。最大树(最小树)不一定是二叉树,节点的子节点个数可以大于2

最大堆(最小堆):最大(最小)完全二叉树。

由于堆是完全二叉树,可以采用公式化描述方案来组织二叉树,采用一维数组来描述堆。利用二叉树的性质,可以将堆中的节点移动到它的父节点或它的一个子节点处。堆是完全二叉树,所以在O(height)时间内能完成的操作,其时间复杂性为O(log(n))

最大堆的插入:当在最大堆中插入一个新元素时,最大堆的结构已经确定,第size位置是新位置,将此位置作为遍历的当前位置并开始沿根的方向前进,按照最大堆的性质,如果当前位置节点的父节点的值大于或者等于插入元素的值,那么可以确定出这个当前位置即为新插入元素的位置,在该位置放置新插入元素即可,否则,将父节点元素移动至该节点,同时当前位置移动到父节点位置。插入策略产生了一条从叶节点到根节点的单一路径,时间复杂性为O(log(n))

最大堆的删除:最大堆的删除操作即删除根节点,同时要维护堆的结构。当删除最大堆的一个元素后,最大堆的结构已经确定。第size位置将是一个因删除一个元素而产生的空位置,需要为该元素在堆中找到放置位置,从根节点(此时根节点元素是要删除的元素)开始,将此位置作为遍历的当前位置,按照最大堆的性质,如果size位置节点元素值大于或者等于当前节点左孩子节点和当前节点右孩子节点,那么可以确定出这个当前位置即为size位置节点元素放置的位置,将元素移入该节点中即可,否则,将当前节点的左右孩子节点中较大元素向上移动至当前节点,同时将当前位置移动到该节点位置。删除策略产生了一条从根节点到叶节点的单一路径,时间复杂性为O(log(n))

最大堆的初始化:

1)  从初始为空的堆中执行n次插入操作来构建堆,时间复杂性为O(n*log(n))

2)  对于已经有元素的完全二叉树,可以通过调整策略将完全二叉树转化为最大堆。倒序遍历完全二叉树,找到第一个具有孩子节点的节点,如果以该节点为根节点的完全二叉树已经是一个最大堆,则不需要调整,否则需要调整使其为一个最大堆,调整策略和最大堆的删除操作类似,(因为最大堆的删除操作之后调整之前的场景是:根节点左右子树都是最大堆,需要调整根节点元素位置)。这种初始化方法得到的时间复杂性为O(n),该处时间复杂性分析有点困难,在此不作分析。

使用堆数据结构来描述优先队列的优缺点:

堆结构是一种隐式的数据结构,堆结构在时间和空间利用率都很高。

有些优化队列的应用使用堆数据结构实现较困难,尤其是需要合并两个优先队列或者多个长度不同的队列时。

 

扩充二叉树:考察一颗二叉树,它有一类特殊的节点叫外部节点,用来代替二叉树中的空子树,其余节点叫内部节点,增加了外部节点的二叉树称为扩充二叉树。

节点S(x):如果X为外部节点,则S(X)0,否则S(X)=min{S(L)S(R)} + 1S(X)为节点X到它所有子树的外部节点的路径中最短的一条。

高度优先左高树:当且仅当一个扩充二叉树任一内部节点的左子树的S值大于或者等于右子树的S值时,该扩充二叉树即为高度优先左高树(height-biased leftist tree - HBLT)。

最大(最小)HBLT:同时又是最大树(最小树)的HBLT

最大HBLT的性质:令X为一个HBLT的内部节点,则

1)  X为根的子树的节点数目至少为:

2)  如子树Xm个节点,S(X)最多为:

3)  通过最右路径,从X到达外部节点的路径长度为S(X)

节点W(X):如果X为外部节点,则W(X)0,如果X为内部节点,则W(X)=W(L)+W(R)+1W(X)为以X为根的子树内部节点数目之和。

重量优先左高树:当且仅当一个扩充二叉树任一内部节点的左子树的W值大于或者等于右子树的W值时,该扩充二叉树即为重量优先左高树(weight-biased leftist tree – WBLT)。

最大(最小)WBLT:同时又是最大(最小)树的WBLT

最大HBLT的合并:待合并的两颗HBLTAB,如果A的根节点元素小于B的根节点元素,则交换AB,始终让A是根节点元素值较大的HBLT,取A的根节点为合并后HBLT的根节点,取A的左子树为合并后HBLT的左子树,A的右子树和B合并后形成的新HBLT为合并后HBLT的右子树,如果合并后HBLT的左子树SX)小于右子树S(X),则交换HBLT的左右子树,保证其为最大HBLT。注意到A的右子树和B合并操作是两个最大HBLT的合并操作,是一个递归操作,采用递归方法来实现两个最大HBLT的合并是很方便的。分析一下最大HBLT合并操作的时间复杂性,合并后最大HBLT的最右路径长度为log(n),最大HBLT的合并实际上是沿最右路径长度的遍历过程,所以时间复杂性为O(n)

最大HBLT的插入:新插入的单一元素构成一个单元素HBLT,插入操作就成了这两颗HBLT的合并操作。

最大HBLT的删除:删除HBLT的根节点即最大值元素,根节点的左子树和右子树是两颗HBLT,删除操作就成了这两颗HBLT的合并操作。

最大HBLT的初始化:

1)  将构建最大HBLT的元素一次插入到一个空的HBLT即可完成最大HBLT的初始化。

2)  将每个元素构建成一个最大HBLT,再两两做最大HBLT的合并操作,继续两两合并的操作,直到只有一个最大HBLT,即完成最大HBLT的初始化。时间复杂性分析和最大堆的初始化类似,有点困难,不作分析,时间复杂性为O(n)

最大堆和最大HBLT总结:最大树完全可以用于最大优先队列,最大二叉树用于最大优先队列时,需要解决插入和删除问题,插入操作可以从二叉树的根开始,比较插入元素和根节点元素,如果根节点元素较大,则选择左孩子节点,再次比较该节点元素和插入元素,递归实现,直到找到一个位置,该位置节点元素小于插入元素,将插入元素放入该位置,同时继续找到该位置节点元素应该的插入位置,递归,直到在最后的叶子节点完成插入操作。删除操作也可以从根节点开始,从根节点左右孩子节点中选择元素值较大的元素放入根节点,然后移动到空置位置处的节点,重复以上过程,直到进行到一个叶子节点完成删除操作。分析上述操作流程,不难发现其中的问题,得到的二叉树是一个非常不平衡的二叉树,可能演变为一个单链表,所以得到的二叉树高度复杂性为O(n),插入和删除操作的性能并不是O(log(n))。而是O(n),和链表、公式化描述线性表时间复杂性一样。最大三叉树、最大四叉树情况类似,不作分析。从以上的分析过程我们可以看到,要保持树较佳的性能,必须能够保证得到的树高度复杂性较佳,所以树的高度决定了某些操作性能,操作性能的分析也就是树的高度的分析过程。以上介绍的二叉树不能取得一个较小的高度值,所以性能较差,而最大堆的高度为log(n+1),最大HBLT最右路径长度最大为log(n+1),在插入和删除操作的过程中,可以有效保证树的左右平衡,也就保证了树的高度的复杂性,能在高度上取得一个较小的值,那么该二叉树性能也较优。既然性能与树的高度有直接联系,那么减小树的高度可以提高性能,减小树的高度的一个方法是三叉树、四叉树等。最大完全四叉树可以采用和最大堆相似的存储结构、插入操作、删除操作,最大四叉HBLT也可以采用和最大HBLT相似的存储结构、插入操作和删除操作,但是最大完全四叉树高度是log4(n+1),最大四叉HBLT最右路径长度最大值为log4(n+1),所以性能较二叉树更优。

 

堆排序问题

从堆的性质不难得出可以使用堆来进行排序,最大堆中,树的根节点元素值最大,且有高效率的插入和删除操作,这样从最大堆的根节点不停的删除元素,就得到一个排序的输出序列。最大堆的初始化时间复杂性为O(n)(采用上述的调整策略),从堆中删除元素的时间复杂性为O(height),那么n个元素全部输出时间复杂性为O(n*log(n)),所以得到堆排序时间复杂性为O(n*log(n))

 

霍夫曼编码问题

外部节点路径长度PL(x):考察一个具有n个外部节点的扩充二叉树,从根节点到该外部节点所路过的边即为该外部节点的路径,边数即为该外部节点的路径长度,即PL(x)

WEPL:考察一个具有n个外部节点的扩充二叉树,每个外部节点具有一个权值即Weight,同时还具有一个外部节点路径长度PL,那么Weighted External Path Length)。

霍夫曼树:如果一个扩充二叉树具有最小WEPL,那么该扩充二叉树即为霍夫曼树。

霍夫曼编码:考察一个具有n个外部节点的霍夫曼树,待编码的元素位于外部节点,该霍夫曼树中,向左路径编码为0,向右路径编码为1,从根节点到外部节点的路径编码序列即为该外部节点元素的霍夫曼编码。因为所有的编码元素位于扩充二叉树的外部节点,从根节点到外部节点的路径是唯一的,且霍夫曼树具有最小WEPL,所以霍夫曼编码是无前缀编码,唯一编码,加权最小编码。

霍夫曼编码压缩和解压缩:文本中出现的字符作为外部节点的元素,文本中字符的频率作为外部节点权值,进行霍夫曼编码,将文本中字符的霍夫曼编码输出即得到文本的霍夫曼压缩。从霍夫曼编码的分析可以看出,在此类型压缩中,霍夫曼编码压缩是最小压缩。由于霍夫曼编码是无前缀编码,那么顺序读取压缩编码并反编码即可得到一个唯一的文本输出。

霍夫曼编码压缩和解压缩问题:

1)  获取不同字符的频率。

2)  建立霍夫曼树并进行霍夫曼编码。

3)  用字符编码代替字符串中字符输出。

4)  为了解压缩,需要保存霍夫曼编码或者字符及其频率信息以在解压缩时重构霍夫曼树。

霍夫曼树的构造:

对构造霍夫曼树的元素建立只有单元素节点的树,此时这些树为霍夫曼树,每次从这些树集合中选择权值最小的两个树分别作为左子树和右子树来构造一个新的树,新树的权值是左子树和右子树的权值之和,将新树其放入集合中,重复该过程直到集合中只有一个树,该树即为霍夫曼树。由于构造霍夫曼树的过程是不停的从集合中选择最小值的元素的过程,所以可以采用最小堆来实现霍夫曼树的构造过程。

猜你喜欢

转载自blog.csdn.net/bedrock_stable/article/details/7575278