Week 10: Priority Queues
Priority Queues
we introduced the queue ADT as a collection of objects that are added and removed according to the first-in, first-out (FIFO) principle. A com- pany’s customer call center embodies such a model in which waiting customers are told “calls will be answered in the order that they were received.” In that setting, a new call is added to the back of the queue, and each time a customer service rep- resentative becomes available, he or she is connected with the call that is removed from the front of the call queue.
In practice, there are many applications in which a queue-like structure is used to manage objects that must be processed in some way, but for which the first-in, first-out policy does not suffice. Consider, for example, an air-traffic control center that has to decide which flight to clear for landing from among many approaching the airport. This choice may be influenced by factors such as each plane’s distance from the runway, time spent waiting in a holding pattern, or amount of remaining fuel. It is unlikely that the landing decisions are based purely on a FIFO policy.
we introduce a new abstract data type known as a priority queue. This is a collection of prioritized elements that allows arbitrary element insertion, and allows the removal of the element that has first priority. When an element is added to a priority queue, the user designates its priority by providing an associ- ated key. The element with the minimal key will be the next to be removed from the queue
The Priority Queue ADT
We model an element and its priority as a key-value composite known as an entry.
- insert(k, v): Creates an entry with key k and value v in the priority queue.
- min( ): Returns (but does not remove) a priority queue entry (k,v) having minimal key; returns null if the priority queue is empty.
- removeMin( ): Removes and returns an entry (k,v) having minimal key from the priority queue; returns null if the priority queue is empty.
- size( ): Returns the number of entries in the priority queue.
- isEmpty( ): Returns a boolean indicating whether the priority queue is empty.
A priority queue may have multiple entries with equivalent keys, in which case methods min and removeMin may report an arbitrary choice among those entry having minimal key. Values may be any type of object.
Priority Queue Operations
Implementing a Priority Queue
One challenge in implementing a priority queue is that we must keep track of both an element and its key, even as entries are relocated within a data structure.
public interface Entry<K, V> {
K getKey();
V getValue();
}
This allows us to return both a key and value as a single object from methods such as min and removeMin. We also define the insert method to return an entry; in a more advanced adaptable priority queue
public interface PriorityQueue<K, V> {
int size();
boolean isEmpty();
Entry<K, V> insert(K key, V value) throws IllegalArgumentException;
Entry<K, V> min();
Entry<K, V> removeMin();
}
Comparing Keys with Total Orders.
In defining the priority queue ADT, we can allow any type of object to serve as a key, but we must be able to compare keys to each other in a meaningful way. More so, the results of the comparisons must not be contradictory.
For a comparison rule, which we denote by ≤, to be self-consistent, it must define a total order relation, which is to say that it satisfies the following properties for any keys k 1 , k 2 , and k 3 :
Comparability property: k 1 ≤ k 2 or k 2 ≤ k 1 .
- Antisymmetric property: if k 1 ≤ k 2 and k 2 ≤ k 1 , then k 1 = k 2 .
- Transitive property: if k 1 ≤ k 2 and k 2 ≤ k 3 , then k 1 ≤ k 3 .
The comparability property states that comparison rule is defined for every pair of keys. Note that this property implies the following one:
- Reflexive property: k ≤ k.
The Comparable Interface
Java provides two means for defining comparisons between object types.
The first of these is that a class may define what is known as the natural ordering of its
instances by formally implementing the java.lang.Comparable interface, which in-
cludes a single method, compareTo. The syntax a.compareTo(b)
must return an
integer i with the following meaning:
- i < 0 designates that a < b.
- i = 0 designates that a = b.
- i > 0 designates that a > b.
For example, the compareTo method of the String class defines the natural ordering of strings to be lexicographic, which is a case-sensitive extension of the alphabetic ordering to Unicode.
In some applications, we may want to compare objects according to some notion other than their natural ordering. For example, we might be interested in which of two strings is the shortest, or in defining our own complex rules for judging which of two stocks is more promising. To support generality, Java defines the java.util.Comparator interface. A comparator is an object that is external to the class of the keys it compares. It provides a method with the signature compare(a, b) that returns an integer with similar meaning to the compareTo method described above.
Eample comparator that evaluates strings based on their length
public class StringLengthComparator implements Comparator<String> {
public int compare(String a, String b) {
if (a.length( ) < b.length( )) return −1;
else if (a.length( ) == b.length( )) return 0;
else return 1;
}
}
For a general and reusable form of a priority queue, we allow a user to choose any key type and to send an appropriate comparator instance as a parameter to the priority queue constructor. The priority queue will use that comparator anytime it needs to compare two keys to each other.
public class DefaultComparator<E> implements Comparator<E> {
public int compare(E a, E b) throws ClassCastException {
return ((Comparable<E>) a).compareTo(b);
}
}
The AbstractPriorityQueue Base Class
To manage technical issues common to all our priority queue implementations, we define an abstract base class named AbstractPriorityQueue. This includes a nested PQEntry class that implements the public Entry interface.
Our abstract class also declares and initializes an instance variable, comp, that stores the comparator being used for the priority queue. We then provide a protected method, compare, that invokes the comparator on the keys of two given entries.
package priorityQueues;
import java.util.Comparator;
public abstract class AbstractPriorityQueue<K, V> implements PriorityQueue<K, V> {
protected static class PQEntry<K, V> implements Entry<K, V> {
private K k; // key
private V v; // value
public PQEntry(K key, V value) {
k = key;
v = value;
}
public K getKey() {
return k;
}
public V getValue() {
return v;
}
protected void setKey(K key) {
k = key;
}
protected void setValue(V value) {
v = value;
}
}
private Comparator<K> comp;
protected AbstractPriorityQueue(Comparator<K> c) {
comp = c;
}
protected AbstractPriorityQueue() {
this(new DefaultComparator<K>());
}
protected int compare(Entry<K, V> a, Entry<K, V> b) {
return comp.compare(a.getKey(), b.getKey());
}
protected boolean checkKey(K key) throws IllegalArgumentException {
try {
return (comp.compare(key, key) == 0); // see if key can be compared to itself
} catch (ClassCastException e) {
throw new IllegalArgumentException("Incompatible key");
}
}
public boolean isEmpty() {
return size() == 0;
}
}
Implementing a Priority Queue with an Unsorted List
In our first concrete implementation of a priority queue, we store entries within an unsorted linked list.
public class UnsortedPriorityQueue<K, V> extends AbstractPriorityQueue<K, V> {
private LinkedPositionalList<Entry<K, V>> list = new LinkedPositionalList<>();
public UnsortedPriorityQueue() {
super();
}
public UnsortedPriorityQueue(Comparator<K> comp) {
super(comp);
}
private Position<Entry<K, V>> findMin() { // only called when nonempty
Position<Entry<K, V>> small = list.first();
for (Position<Entry<K, V>> walk : list.positions())
if (compare(walk.getElement(), small.getElement()) < 0)
small = walk;
return small;
}
@Override
public Entry<K, V> insert(K key, V value) throws IllegalArgumentException {
checkKey(key);
Entry<K, V> newest = new PQEntry<>(key, value);
list.addLast(newest);
return newest;
}
@Override
public Entry<K, V> min() {
if (list.isEmpty())
return null;
return findMin().getElement();
}
@Override
public Entry<K, V> removeMin() {
if (list.isEmpty())
return null;
return list.remove(findMin());
}
@Override
public int size() {
return list.size();
}
public void TraversePositionalList() {
for (Position<Entry<K, V>> walk : list.positions())
System.out.println(walk.getElement().getValue());
}
public void TraversePriorityQueue() {
while (list.size() > 0) {
System.out.println(removeMin().getValue());
}
}
public static void main(String[] args) {
UnsortedPriorityQueue<Integer, String> upq = new UnsortedPriorityQueue<>();
upq.insert(2, "ayman");
upq.insert(4, "ahmed");
System.out.println(upq.min().getValue());
upq.insert(1, "farida");
System.out.println(upq.min().getValue());
upq.TraversePriorityQueue();
}
}
Implementing a Priority Queue with an Sorted List
package priorityQueues;
import java.util.Comparator;
import positionalList.LinkedPositionalList;
import trees.Position;
import positionalList.PositionalList;
public class SortedPriorityQueue<K, V> extends AbstractPriorityQueue<K, V> {
private LinkedPositionalList<Entry<K, V>> list = new LinkedPositionalList<>();
public SortedPriorityQueue() {
super();
}
public SortedPriorityQueue(Comparator<K> comp) {
super(comp);
}
@Override
public int size() {
return list.size();
}
public Entry<K, V> insert(K key, V value) throws IllegalArgumentException {
checkKey(key);
Entry<K, V> newest = new PQEntry<>(key, value);
Position<Entry<K, V>> walk = list.last();
while (walk != null && compare(newest, walk.getElement()) < 0)
walk = list.before(walk);
if (walk == null)
list.addFirst(newest);
else
list.addAfter(walk, newest);
return newest;
}
@Override
public Entry<K, V> min() {
if (list.isEmpty())
return null;
return list.first().getElement();
}
@Override
public Entry<K, V> removeMin() {
if (list.isEmpty())
return null;
return list.remove(list.first());
}
public void TraversePriorityQueue() {
for (Position<Entry<K, V>> walk : list.positions())
System.out.println(walk.getElement().getValue());
}
public static void main(String[] args) {
SortedPriorityQueue<Integer, String>spq = new SortedPriorityQueue<>();
spq.insert(2, "ayman");
spq.insert(4, "ahmed");
System.out.println(spq.min().getValue());
spq.insert(1, "farida");
System.out.println(spq.min().getValue());
spq.TraversePriorityQueue();
}
}