Priority Queues
Steven J. Zeil
Problem: Given a collection of elements that carry a numeric “score”, find and remove the element with the smallest [largest] score. New elements may be added at any time.
This collection is called a priority queue because,
-
like queues, it is used to simulate objects awaiting a service.
-
But instead of FIFO, the processing order is determined by the object’s individual “priority” or score.
1 The std Priority Queue Interface
This is the entire priority queue interface.
template <class T, class Container, class Compare>
class priority_queue
{
public:
typedef T value_type;
typedef Container::size_type size_type;
protected:
Container c;
Compare comp;
public:
priority_queue(const Compare& x = Compare());
template <class Iterator>
priority_queue(Iterator first,
Iterator last,
const Compare& x = Compare());
bool empty() const;
size_type size() const;
value_type& top();
const value_type& top() const;
void push(const value_type& x);
void pop();
};
-
We can check the
size
of the priority queue or ask if it isempty
. -
We can look at the
top
(largest) element. -
We can remove the largest element by
pop
’ping. -
We can
push
a new element into the priority queue. Unlike pushing onto a stack or queue, however, the element does not automatically become the first or last thing we will next retrieve. Exactly when we will see this element again depends on its priority value.
1.1 priority_queue is an Adapter
template <class T, class Container, class Compare>
class priority_queue
{
public:
⋮
Like std::stack and std::queue, priority_queue is an adapter that works on an underlying sequential container, which must provide random-access iterators (vector
or deque
). For example,
priority_queue<Event, vector<Event> > myPQueue;
2 Applications of Priority Queues
Priority queues tend to crop up in a lot of algorithms as “helpers” for working with other data structures. In particular, we’ll see them in connection with a lot of algorithms for finding optimal paths and doing other work with graphs.
Priority queues are often useful in scheduling. For example, in discrete event simulation, we simulate real-world systems as a series of events that are scheduled to take place at some future time. For example, if we wanted to simulate automobile traffic on downtown streets, we might create a whole collection of events, some of which spawn other future events:
-
Each traffic light in our simulation would have an event scheduled for its next change. When a light changes to red, we schedule it’s next change to green some number of seconds later. When a light changes to green, we schedule a change-to-yellow event some time a little later, and so on.
-
Parking lots and garages might be simulated as objects that, at random time intervals, toss a new car out on to the street in front of the lot or garage. The average size of the car-generation interval would vary with the time of day (e.g., at 5:00PM, when everyone is leaving work, the interval between cars leaving each garage would be reduced).
-
Each moving car on the street would have an event associated with the time it needs to reach the next intersection along its path. When it reaches tht intersection, we schedule a new event for the intersection after that, and so on.
All scheduled events could be kept in a (min) priority queue ordered on the time at which the event is scheduled. The main logic of the simulation is simply
priority_queue<Event, vector<Event> > futureEvents;
⋮
while (simulation has not ended)
{
get next event from futureEvents;
trigger that event;
}
Each distinct type of event would have its own “trigger” function, many of which are likely to add one or more additional events to the priority queue. What’s essential is that we always be able to get to the next scheduled event each time around that main loop. When a new event gets added, it might stay far down in the queue (if the new event is a long time in the future) or might spring to the front of the queue if there is nothing more immediate. For example, suppose we have a single car speeding down Main Street at 3AM. Each of its “will-reach-next-intersection” events is likely to go immediately to the first position in the queue, unless the traffic lights are cycling very quickly.