Lecture 7

Priority Queues

A priority queue is a data structure for maintaining a set of elements, each with an associated value called a key. In a normal queue, elements come off in first-in-first-out order, so the first element in the queue is the top element. In a priority queue, the element with the largest key is always on the top, no matter what order it or the other elements were inserted. Some uses for priority queues:

Priority Queue Operations

The following operations are defined on priority queues (assumed to be initially empty sets):

Naive Implementation

A naive implementation of these operations is to represent the queue as a linked list L:

Heap Implementation

We can use a heap to improve the implementation, since a heap always keeps its maximum element in the first element: Let's compare the two implementations in a table:

Naive vs. Heap-based Priority Queues
Type of Queue Time for Maximum Time for Insert Time for Extract-Max
Linked List (n) (1) (n)
Heap Based (1) O(lg n) O(lg n)

Using a heap-based priority queue looks good, but should it bother us that Heap-Insert takes O(lg n) time while Naive-Insert takes only (1) time? No: Most algorithms that use a priority queue, like those mentioned above, will either leave the queue empty at the end or reach a steady state where the same number of items enter the queue as leave the queue. Thus, for every call to Insert, there must be a corresponding call to Extract-Max. Any advantage the naive implemenation gains with its (1) Insert is quickly lost when it has to do a (n) Extract-Max.

Also, a linked list may grow arbitrarily large, while the heap owes much of its speed advantage to its implementation in a fixed-sized array. We may, from time to time, have to reallocate the heap's array to accomodate more elements. Reallocating may take O(n) time. Will this eat up our (lg n) advantage with Insert? Yes, if we have to reallocate every time we do an Insert. But if we are smart, we will double the size of the array each time we reallocate, anticipating future Inserts. This will allow us to do only up to O(lg n) reallocations during a program run, preserving the heap's asymptotic advantage.

Note: if the number of different priorities is a small constant k, then using an implementation based on having an array of k linked lists might give better performance than the heap-based priority queue. Such is the case with operating system schedulers with a small number of priorities, e.g., "system," "user," and "background" (k=3).