Data Structures for Java
William H. Ford William R. Topp
Chapter 15
Queues and Priority Queues
Bret Ford
© 2005, Prentice Hall
Queue Collection
• A queue is a list of items that allows for
access only at the two ends of the sequence, called the front and back of the queue. An item enters at the back and exits from the front.
•
• A waiting line at a grocery store or a bank is a model
of a stack.
Queue Operations
• Queue operations are restricted to the ends of the list, called front and back.
push(item) adds item at the back
pop() removes element from the front
peek() accesses value at the front
Queue Operations (cont)
• An item removed (pop) from the queue
is the first element that was added (push) into the queue. A queue has FIFO (first-in-first-out) ordering.
• Queue inserts followed by queue deletions maintain the order
of the elements
Queue Interface
• The generic Queue interface defines a restricted set of collection operations that access and
update elements only at the end of the list.
Queue Interface
(concluded)
LinkedQueue Class
• The Queue interface is an adapter which defines a restricted set of
list methods. A Queue class can be efficiently implemented using
a linked list as the storage structure. The LinkedQueue class uses a
LinkedList collection and composition.
LinkedQueue Class (continued)
public class LinkedQueue<T> implements Queue<T>
{
private LinkedList<T> qlist = null;
public LinkedQueue () {
qlist = new LinkedList<T>();
}
. . . }
Implementing LinkedQueue Method pop()
• Method has runtime efficiency O(1)
public T pop() {
// if the queue is empty, throw // NoSuchElementException
if (isEmpty())
throw new NoSuchElementException(
"LinkedQueue pop(): queue empty");
// remove and return the first element in the list return qlist.removeFirst();
}
Program 15.1
• The program implements an interview scheduler
that is a queue of Time24 objects. Output lists the
times and the potential length of each interview.
Program 15.1 (continued)
import java.io.*;
import java.util.Scanner;
import ds.util.LinkedQueue;
import ds.time.Time24;
public class Program15_1 {
public static void main(String[] args) throws IOException
{
final Time24 END_DAY = new Time24(17,00);
String apptStr;
// time interval from current appt to next appt Time24 apptTime = null, interviewTime = null;
// input stream to read times as strings from // file "appt.dat"
Scanner input = new Scanner(
new FileReader("appt.dat"));
Program 15.1 (continued)
// queue to hold appointment time // for job applicants
LinkedQueue<Time24> apptQ = new LinkedQueue<Time24>();
// construct the queue by appt times as // strings from file; use parseTime to // convert to Time24 object
while (input.hasNext()) {
apptStr = input.nextLine();
apptQ.push(Time24.parseTime(apptStr));
}
// output the day's appointment schedule
System.out.println("Appointment Interview");
Program 15.1 (continued)
// pop next appt time and determine // available time for interview (peek // at next appt at front of queue) while (!apptQ.isEmpty())
{
// get the next appointment apptTime = apptQ.pop();
// interview time is interval to next // appt or to END_DAY
if (!apptQ.isEmpty())
interviewTime = apptTime.interval(
apptQ.peek());
else
interviewTime = apptTime.interval(END_DAY);
Program 15.1 (concluded)
// display appointment time and interview time System.out.println(" " + apptTime +
" " + interviewTime);
} } }
Program 15.1 (Run)
File "appt.dat":
10:00 11:15 13:00 13:45 14:30 15:30 16:30 Run:
Appointment Interview 10:00 1:15 11:15 1:45 13:00 0:45 13:45 0:45 14:30 1:00 15:30 1:00 16:30 0:30
Radix Sort
• The radix sort is a linear algorithm that sorts an array of integers. It uses successive digits in the numbers to partially sort the list.
• Successive elements in the array are passed
into an array of ten queues (index 0 to 9) using
a digit as the index. Elements are copied from
the array back to the array, creating a partially
sorted list.
Radix Sort Example
• List: [91, 6, 85, 15, 92, 35, 30, 22, 39]
After Pass 0: [30, 91, 92, 22, 85, 15, 35, 6, 39]
After Pass 2: [6, 15, 22, 30, 35, 39, 85, 91, 92]
Radix Sort (continued)
• Any two numbers with different ten's digits,
such as 35 and 92 will be in the proper relative order since the algorithm collects numbers
from the 3-bin before numbers from the 9-bin.
• Pass 0 assures the ordering of numbers with
the same tens digit. Such numbers will appear
in the tens queue in order of their ones digit.
Radix Sort (continued)
Initial sequence: {91, 6, 85, 15, 92, 35, 30, 22, 39}
Same tens digit. In order by ones digit
Radix Sort Method
• The radixSort() method uses the expanded base-10 representation of a number.
value = x
d-110
d-1+ x
d-210
d-2+ ... + x
210
2+ x
110
1+ x
010
0• The implementation uses 10 queues // allocate 10 null references to a LinkedQueue LinkedQueue[] digitQueue = new LinkedQueue[10];
// initialize each element of digitQueue to be // an empty queue
for (i=0;i < digitQueue.length;i++)
digitQueue[i] = new LinkedQueue();
Radix Sort Method (continued)
• The radixSort() method must execute d
iterations, one for each digit. The i-th iteration distributes the numbers into the array of
queues, digitQueue, by using the digit
corresponding to the power 10
i. To determine the value of the digit at any position i, divide a number by the power = 10i and take the
remainder after division by 10.
digitQueue[(arr[i] / power) % 10].push(new Integer(arr[i]));
Radix Sort distribute()
// support method for radixSort()
// distribute array elements into one of 10 queues // using the digit corresponding to power
// power = 1 ==> 1's digit // power = 10 ==> 10's digit // power = 100 ==> 100's digit // ...
private static void distribute(int[] arr, LinkedQueue[] digitQueue, int power)
{
int i;
// loop through the array, inserting each
// element into the queue (arr[i] / power) % 10 for (i = 0; i < arr.length; i++)
digitQueue[(arr[i] / power) % 10].push(arr[i]);
}
Radix Sort collect()
// support method for radixSort()
// gather elements from the queues and copy // back to the array
private static void collect(
LinkedQueue[] digitQueue, int[] arr) {
int i = 0, digit;
// scan the array of queues using // indices 0, 1, 2, etc.
for (digit = 0; digit < 10; digit++) // collect items until queue empty // and copy items back to the array while (!digitQueue[digit].isEmpty()) {
arr[i] = digitQueue[digit].pop());
i++;
} }
radixSort() Method
public static void radixSort(int[] arr, int d) {
int i;
// current digit found by dividing by 10^power int power = 1;
// allocate 10 null references to a LinkedQueue LinkedQueue[] digitQueue = new LinkedQueue[10];
// initialize each element of digitQueue to be // an empty queue
for (i=0; i < digitQueue.length; i++) digitQueue[i] = new LinkedQueue();
for (i=0; i < d; i++) {
distribute(arr, digitQueue, power);
collect(digitQueue, arr);
power *= 10;
} }
radixSort() Method (concluded)
Runtime efficiency of radixSort() is O(dn) Runtime efficiency of radixSort() is O(dn) where the array has n elements and the where the array has n elements and the
maximum integer has d digits.
maximum integer has d digits.
Program 15.2
• The program illustrates radixSort() for an array
of 50 integers in the range 0-99,999.
Program 15.2 (continued)
import java.util.Random;
import ds.util.Arrays;
public class Program15_2 {
public static void main(String[] args) {
// array to hold the data that is sorted int[] arr = new int[50];
Random rnd = new Random();
int i;
// initialize array with 50 random numbers in // range 0 - 99999
for (i = 0; i < 50; i++)
arr[i] = rnd.nextInt(100000);
// apply the radix sort and output // the sorted array
Arrays.radixSort(arr, 5);
displayArray(arr);
Program 15.2 (continued)
private static void displayArray(int[] arr) {
int i, j, strnLength;
String s, strn;
for (i=0; i < arr.length; i++) {
// represent value of arr[i] as a string strn = String.valueOf(arr[i]);
// capture the length of strn strnLength = strn.length();
s = "";
// justify strn in a field // of 8 print positions
for (j=0; j < 8-strnLength; j++) s += " ";
s += strn;
Program 15.2 (concluded)
// output the justified integer value System.out.print(s);
// newline every 6 numbers if ((i+1) % 6 == 0)
System.out.println();
}
System.out.println();
} }
Program 15.2 (Run)
RUN
2554 3097 5231 6876 8539 12446 16483 20040 23202 24353 24758 25996 28922 29730 30672 32032 32198 32261 36705 36867 47340 47688 51547 53617 54797 55577 56055 59553 61588 65289 65465 68416 68935 71586 73017 77119 80185 80659 81371 83443 87678 88138 90076 90717 93637 94948 95470 96984 97332 98616
Bounded Queue
• A bounded queue is a queue that can contain a fixed number of elements. An insert into the
queue can occur only when the queue is not already full.
• The BQueue class implements a bounded queue. The class implements the Queue
interface. The boolean method full() indicates whether the queue is full.
• The class uses an array to store the elements.
BQueue Class API
BQueue Class Example
• The example illustrates the declaration of a
BQueue object and the use of full() to avoid
attempting an insertion into a full queue. An
exception occurs when we call push() from
within a try block and attempt to add an
element to a full queue.
BQueue Class Example (continued)
// declare an empty bounded queue with fixed size 15 BQueue<Integer> q = new BQueue<Integer>(15);
int i;
// fill-up the queue
for (i=1; !q.full(); i++) q.push(i);
// output element at the front of q and the queue size System.out.println(q.peek() + " " + q.size());
try {
q.push(40); // exception occurs }
catch (IndexOutOfBoundsException iobe)
BQueue Class Example (concluded)
Output:
1 15
java.lang.IndexOutOfBoundsException: BQueue push(): queue full
BQueue Class
public class BQueue<T> implements Queue<T>
{
// array holding the queue elements private T[] queueArray;
// index of the front and back of the queue private int qfront, qback;
// the capacity of the queue and the current size private int qcapacity, qcount;
// create an empty bounded queue with specified size public BQueue(int size)
{
qcapacity = size;
queueArray = (T[])new Object[qcapacity];
qfront = 0;
qback = 0;
qcount = 0;
}
BQueue Class (concluded)
public BQueue() {
// called non-default constructor // with capacity = 50
BQueue(50);
}
< method full() and methods in the Queue interface >
}
BQueue Class Implementation
No room for E.
Need a way to use
the slots at indices 0, 1.
BQueue Class Implementation (continued)
• Think of the queue as a circular sequence with a series of slots that allow element to enter in a clockwise
fashion. The element at index qfront exits the queue
and an element enters the queue at index qback.
BQueue Class Implementation (continued)
• Treating the array as a circular sequence involves
updating qfront and qback to cycle back to the front the array as soon as they move past the end of the array.
Move qback forward: qback = (qback + 1) % qcapacity;
Move qfront forward: qfront = (qfront + 1) % qcapacity;
BQueue Class full()
public boolean full() {
return qcount == qcapacity;
}
BQueue Class push()
public void push(T item) {
// is queue full? if so, throw an // IndexOutOfBoundsException
if (qcount == qcapacity)
throw new IndexOutOfBoundsException(
"BQueue push(): queue full");
// insert into the circular queue queueArray[qback] = item;
qback = (qback+1) % qcapacity;
// increment the queue size qcount++;
}
BQueue Class pop()
public T pop() {
// if queue is empty, throw a // NoSuchElementException
if (count == 0)
throw new NoSuchElementException(
"BQueue pop(): empty queue");
// save the front of the queue
T queueFront = queueArray[qfront];
// perform a circular queue deletion qfront = (qfront+1) % qcapacity;
// decrement the queue size qcount--;
// return the front return queueFront;
}
Priority Queue Collection
• A priority queue is a collection in which all elements have a comparison (priority) ordering.
• It provides only simple access and update operations where a deletion always removes the element of
highest priority
PQueue Interface
• The generic PQueue resembles a queue
with the same method names.
PQueue Interface
(concluded)
HeapPQueue Class
• The collection class HeapPQueue implements the PQueue interface.
– By default, the element of highest priority is the one with the largest value (a maximum priority queue); that is, if x and y are two elements in a
priority queue and x > y, then x has higher priority
than y.
HeapPQueue Class Example
// create an empty priority queue of generic type String HeapPQueue<String> pq = new HeapPQueue<String>();
int n;
pq.push("green");
pq.push("red");
pq.push("blue");
// output the size and element with the highest priority System.out.println(pq.size() + " " + pq.peek());
// use pop() to clear the collection and list elements in // priority (descending) order
while (!pq.isEmpty())
System.out.print(pq.pop() + " ");
Output:
3 red
red green blue
Support Services Pool
• The application processes job requests to a company
support service pool. A request has a job ID, a job status, and a time requirement.
• JobStatus is an enum with a listing of employee categories with values that allows for comparison of objects.
• The JobRequest class implements Comparable and describes job objects.
•
Support Services Pool (continued)
enum JobStatus {
clerk (0), manager (1), director(2), president(3);
int jsValue;
JobStatus(int value) { jsValue = value; } public int value() { return jsValue; } }
Support Services Pool (continued)
Support Services Pool (continued)
Program 15.3
• The program processes job requests with
different employee statuses. Output lists the
jobs by status along with their total time.
Program 15.3 (continued)
import java.io.*;
import java.util.Scanner;
import ds.util.HeapPQueue;
public class Program15_3 {
public static void main(String[] args) throws IOException
{
// handle job requests
HeapPQueue<JobRequest> jobPool = new HeapPQueue<JobRequest>();
// job requests are read from file "job.dat"
Scanner sc = new Scanner(new FileReader(
"job.dat"));
Program 15.3 (continued)
// time spent working for each category // of employee
// initial time 0 for each category int[] jobServicesUse = {0,0,0,0};
JobRequest job = null;
// read file; insert each job into // priority queue
while ((job = JobRequest.readJob(sc)) != null) jobPool.push(job);
// delete jobs from priority queue // and output information
System.out.println("Category Job ID" + " Job Time");
Program 15.3 (continued)
while (!jobPool.isEmpty()) {
// remove a job from the priority // queue and output it
job = (JobRequest)jobPool.pop();
System.out.println(job);
// accumulate job time for the // category of employee
jobServicesUse[job.getStatus().value()] +=
job.getJobTime();
}
System.out.println();
writeJobSummary(jobServicesUse);
}
Program 15.3 (concluded)
private static void writeJobSummary(
int[] jobServicesUse) {
System.out.println("Total Pool Usage");
System.out.println(" President " + jobServicesUse[3]);
System.out.println(" Director " + jobServicesUse[2]);
System.out.println(" Manager " + jobServicesUse[1]);
System.out.println(" Clerk " + jobServicesUse[0]);
} }
Program 15.3 (Run)
Run
Category Job ID Job Time President 303 25 President 306 50 Director 300 20 Director 307 70 Director 310 60 Director 302 40 Manager 311 30 Manager 304 10 Manager 305 40 Clerk 308 20 Clerk 309 20 Clerk 301 30 Total Pool Usage
President 75 Director 190 Manager 80
Event-Driven Simulation
• An event-driven simulation uses a priority
queue to store events that occur over time as the simulation unfolds.
• The bank simulation looks a stream of
customers that are served by two tellers.
Different event types deal with arrival, service, and departure of a customer. Arrival times
and service times are random.
Event-Driven Simulation (continued)
• The graphic illustrates events (A
i= arrival, S
i= service,
D
i= departure) for four customers. Solid line is service
and dotted line is waiting time.
Event Classes
• Event classes are defined in an inheritance hierarchy.
The abstract base class Event compares the time
events occurs and defines the method doEvent()
which is implemented in each subclass.
Event Class
public abstract class Event implements Comparable<Event>
{
protected int time;
// constructor sets time for the event public Event(int t)
{ time = t; }
abstract void doEvent(); // describes activity
Event Class (concluded)
// used to order the events (elements) // in a priority queue
public int compareTo(Event e) {
if(time < e.time) return -1;
else if (time == e.time) return 0;
else
return 1;
} }
EventDrivenSimulation Class
import ds.util.HeapPQueue;
public class EventDrivenSimulation {
// minimum heap pops event elements // in order of their time
private HeapPQueue<Event> eventQueue =
new HeapPQueue<Event>(new Less<Event>());
// adds an Event object to the queue public void pushEvent(Event e)
{ eventQueue.push(e); }
EventDrivenSimulation Class (concluded)
// runs simulation by processing events // as they exit the priority queue
public void run() {
while (!eventQueue.isEmpty()) {
// extract event and process it // with doEvent()
Event nextEvent = eventQueue.pop();
nextEvent.doEvent();
} } }
BankSimulation Class
public class BankSimulation extends EventDrivenSimulation
{
// parameters used to describe the simulation int simulationLength; // simulation length int numTellers;
int arrivalLow, arrivalHigh; // next arrival range int serviceLow, serviceHigh; // service range
// variables used to monitor the simulation int numCustomers = 0;
int totalWaitTime = 0;
// used for delay between arrivals int prevArrTime = 0;
// detail each event?
boolean verboseRun = false;
// use for random times Random rnd = new Random();
// list of tellers
Teller[] tList = null;
BankSimulation Class (concluded)
// key method inputs parameters, creates events, // runs simulation and outputs results
public void startSimulation() { . . . }
}
startSimulation()
public void startSimulation() {
// read simulation parameters from keyboard inputParameters();
// create instances for each teller;
// for convenience, tellers are referenced // with indices beginning at 1
for (int i = 1; i <= numTellers; i++) tList[i] = new Teller();
startSimulation() (continued)
// for the length of the simulation, create // successive arrival events at random arrival // times; push events on the priority queue // and update prevArrTime
int t = 0;
while (t < simulationLength) {
// randomTime() returns random integer // in the range arrivalLow to arrivalHigh t += randomTime(arrivalLow, arrivalHigh);
if (t >= simulationLength) break;
// create an arrival event and add it // to priority queue
pushEvent(new ArrivalEvent(t, prevArrTime));
// update prevArrTime for use by next arrival prevArrTime = t;
}
startSimulation() (concluded)
// with arrival events loaded in the priority // queue, begin execution of the simulation;
// Note: during execution, the queue will
// dynamically grow since when an arrival event // exits the queue, its doEvent() method adds // a service event to the queue; when a service // event exits the queue, its doEvent() method // adds a departure event
run();
// display a summary of results displayResults();
}
ArrivalEvent Class
private class ArrivalEvent extends Event {
private int elapsedTime;
public ArrivalEvent(int time, int prevArrTime) {
super(time);
elapsedTime = time - prevArrTime;
}
public void doEvent() {
int i, minTeller, serviceTime;
DecimalFormat numberFmt = new DecimalFormat("00");
// increment number of customers numCustomers++;
ArrivalEvent Class (continued)
// use elapsedTime to update backService // for each teller; return teller with // minimum backService; this is the next // available teller
minTeller = minTellerService(tList, elapsedTime);
// backService for teller is wait // time for customer
totalWaitTime += tList[minTeller].backService;
// generate service time for customer;
// add to backService for minTeller who // will serve customer
serviceTime = randomTime(serviceLow, serviceHigh);
tList[minTeller].backService += serviceTime;
ArrivalEvent Class (continued)
if (verboseRun) . . .
// create ServiceEvent object and add // to priority queue
pushEvent(new ServiceEvent(
time + tList[minTeller].backService, numCustomers,
minTeller,
serviceTime));
}
private int minTellerService(Teller[] tList, int elapsedTime)
{
int i, minTeller = 1;
ArrivalEvent Class (concluded)
for (i = 1; i <= numTellers; i++)
tList[i].backService = (tList[i].backService - elapsedTime <= 0) ? 0 :
tList[i].backService - elapsedTime;
for (i = 2; i <= numTellers; i++) if (tList[i].backService <
tList[minTeller].backService) minTeller = i;
return minTeller;
} }
Program 15.4
public class Program15_4 {
public static void main(String[] args) {
// create bank simulation object and start it up BankSimulation bank = new BankSimulation();
bank.startSimulation();
} }
Program 15.4 (Run #1)
Run 1:
Use verbose run ('Y' or 'N'): Y
Enter the simulation time in minutes: 40 Enter number of available tellers: 2
Enter range of potential arrival times: 3 7 Enter range of potential service times: 5 11 Customer #01 Arrival 6 Wait 6
Customer #01 Begin service at 12 by teller 1 Service time 6 Customer #02 Arrival 13 Wait 8
Customer #01 Departs 18 Served by 1 Customer #03 Arrival 18 Wait 8
Customer #02 Begin service at 21 by teller 1 Service time 8 Customer #04 Arrival 22 Wait 11
Customer #03 Begin service at 26 by teller 2 Service time 8 Customer #02 Departs 29 Served by 1
Program 15.4
(Run #1, concluded)
Customer #05 Arrival 29 Wait 9
Customer #04 Begin service at 33 by teller 1 Service time 11 Customer #03 Departs 34 Served by 2
Customer #06 Arrival 36 Wait 6
Customer #05 Begin service at 38 by teller 2 Service time 9 Customer #06 Begin service at 42 by teller 1 Service time 6 Customer #04 Departs 44 Served by 1
Customer #05 Departs 47 Served by 2 Customer #06 Departs 48 Served by 1 Simulation Summary
Number of customers is 6
Average waiting time is 0.0 minutes Service time for tellers
Teller 1: Busy 64.6% Overtime 8 Teller 2: Busy 36.2% Overtime 7
Program 15.4 (Run #2)
Run 2:
Use verbose run ('Y' or 'N'): N
Enter the simulation time in minutes: 480 Enter number of available tellers: 3
Enter range of potential arrival times: 1 5 Enter range of potential service times: 4 11 Simulation Summary
Number of customers is 156
Average waiting time is 1.0 minutes Service time for tellers
Teller 1: Busy 88.4% Overtime 12 Teller 2: Busy 80.0% Overtime 10 Teller 3: Busy 65.3% Overtime 19