数据结构与算法(6):优先队列

介绍

优先队列是一种抽象数据类型,它表示了一组值和对这些值的操作,优先队列最重要的操作就是删除最大元素和插入元素。其API如下:

 

初级实现

数组实现(无序)

类似栈的数组实现。inser()方法的代码和栈的push()方法完全一样。要实现删除最大元素,我们可以添加一段类似于选择排序的内循环的代码,将最大元素和边界元素交换然后删除它。和栈类似,我们也可以加入调整数组大小的代码来保证数据结构中至少含有四分之一的元素而又永远不会溢出。

数组实现(无序)

另一种方法就是在insert()方法中添加代码,将所有较大的元素向右边移动一格以使数组保持有序(和插入排序一样)。这样,最大的元素总会在数组的一边,优先队列的删除最大元素操作就和栈的pop()操作一样了。

链表表示法

和刚才类似,我们可以用基于链表的栈的代码作为基础,而后可以选择修改pop()来找到并返回最大元素,或是修改push()来保证所有元素为逆序并用pop()来删除并返回链表的首元素(也就是最大的元素)。

总结

对于栈和队列,我们的实现能够在常数时间内完成所有的操作;而对于优先队列,我们刚刚讨论过的所有初级实现中,插入元素和删除最大元素这两个操作之一在最坏情况下需要线性时间来完成。

堆的实现

数据结构二叉堆能够很好地实现优先队列的基本操作。如果我们用指针来表示堆有序二叉树,那么每个元素都需要三个指针来找到它的上下结点(父结点和两个子结点各需要一个)。如果我们使用完全二叉树,表达就会变得特别方便。具体方法是将二叉树的结点按照层级顺序放入数组中。根结点在位置1,它的子结点在位置2和3,而子结点的子结点则分别在位置4、5、6和7,以此类推。用数组(堆)实现的完全二叉树的结构是很严格的,但它的灵活性已经足以让我们高效地实现优先队列。用它们我们将实现对数级别的插入元素和删除最大元素的操作。

命题P:一棵大小为N的完全二叉树的高度为\left \lfloor lgN \right \rfloor

我们用长度N+1的数组pq表示一个大小为N的堆,我们不会使用pq[0],堆元素放在pq[1]至pq[N]中。堆的操作首先会进行一些简单的改动,打破堆的状态,然后再遍历堆并按照要求将堆的状态修复。我们称这个过程叫做堆的有序化

由下至上的堆有序化(上浮)

如果堆的有序状态因为某个结点变得比它的父结点更大而被打破,那么我们就需要通过交换它和它的父结点来修复堆。交换后,这个结点比它的两个子结点都大。但这个结点仍然可能比它现在的父结点更大。我们可以一遍遍地用同样的方法恢复秩序,直到我们遇到一个更大的父结点。只要记住位置k的结点的父结点的位置是\left \lfloor k / 2 \right \rfloor

由上至下的堆有序化(下沉)

如果堆的有序状态因为某个结点变得比它的两个子结点或是其中之一更小而被打破了,那么我们可以通过将它和它的两个子结点中的较大者交换来恢复堆。类似的上浮,这个过程需要一遍遍重复,将结点向下移动直到它的子结点都比它更小或是到达了堆的底部。

插入元素

我们将新元素加到数组末尾,增加堆的大小并让这个新元素上浮到合适的位置。

删除最大元素

我们从数组顶端删去最大的元素并将数组的最后一个元素放到顶

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值