优先队列(Priority Queue)的方法包括;
优先队列的特点是:你可以把它当成一袋东西,然后你可以向里面添加item,或者得知它共有多少个item,此外,供你交互的只有队列里的最小的item。
以如下的例子说明应该在何时使用优先队列:
假设你需要收集24小时的消息,然后挑出m条最不和谐(unharmonious)的信息出来。一种通常的解法是:
即收集24小时的全部消息,然后对其进行排序,这会使得占用巨大的内存(O(N)).而使用PQ(Priority queue)则可将内存消耗降低为O(M).其解法如下:
首先开始添加消息,并使用优先队列,当添加到m个消息时,每再添加一个,就将优先队列中的最小消息删去,这样就可一直保持内存消耗,并且最终留下的m条消息即为所求。
接下来我们将利用Heap(堆)去实现优先队列,以下是目前所有学过的数据结构对于实现优先队列的各种操作的时间消耗:
堆(heap)的定义强调两种属性:
- min—heap,即堆的最小属性。这指的是若用二叉树实现堆,则每个节点的值必须小于或者等于其所有子节点的值
- Complete,即完整性。这指的是若用二叉树实现堆,则若有item缺失的情况,只能发生在bottom level(即只能是每个子树的倒数第二层的节点对应的子节点没有满),并且所有节点尽可能地向左进行倾斜
一些heap的例子:
利用这种heap(最小堆)实现PQ具有天然的优势:
- 对于getSmallest(),直接返回heap的根节点即可
- 对于add() ,以下展示了两种解决情况:
首先,上述堆是合法的,既满足min性质,也满足compelete性质,我们要添加3,则需要添加在首先保持满足compelete性质的位置(如下图所示):
此时,再举一个添加"5"的例子:
- 对于removesmallest() ,以下同样展示了两种解决情况:
利用堆(heap)去实现优先队列的所有操作方法:
这里面着重注意一下add
操作中的last position
以及removeSmallest
操作中的the rightmost person
指的是哪个位置!!!!!!!!!!
在各种编程语言中,java普遍有这几种实现形式:
不同于上述三种方法,以下这种方法通过维护两个Array(key数组和parents数组)来表示树结构:
而事实上,我们可以不去维护parents数组,而只去维护keys数组,以表示整个树结构,如下图所示:
那么在去掉了parents数组后,我们如何得知某一节点的父节点是谁呢(譬如我输入1或者2,需要返回0;输入3或者4,需要返回1.......),答案如下图;
事实上,我们稍微将key数组的定义方式再小小的改变一下,就可以方便的检索任意item的左,右子节点以及父节点;