ch04 Queues
ch04 Queues
Queues
Chapter Objectives
To learn how to represent a waiting line (queue) and how to
use the methods in the Queue interface for insertion (offer
and add), removal (remove and poll), and for accessing the
element at the front (peek and element)
To understand how to implement the Queue interface using a
single-linked list, a circular array, and a double-linked list
To become familiar with the Deque interface and how to use
its methods to insert and remove items from either end of a
deque
To understand how use Queues and random number
generators to simulate the operation of a physical system
that has one or more waiting lines
Queue
The queue, like the stack, is a widely used data
structure
A queue differs from a stack in one important way
A stack is LIFO list – Last-In, First-Out
while a queue is FIFO list, First-In, First-Out
Queue Abstract Data Type
Section 4.1
Queue Abstract Data Type
A queue can be visualized as a line of customers waiting for
service
The next person to be served is the one who has waited the
longest
New elements are placed at the end of the line
Print Queue
Operating systems use queues to
keep track of tasks waiting for a scarce resource
ensure that the tasks are carried out in the order they were
generated
Print queue: printing is much slower than the process of
selecting pages to print, so a queue is used
Unsuitability of a Print Stack
Stacks are Last-In, First-Out (LIFO)
The most recently selected document would be the
next to print
Unless the printer stack is empty, your print job may
never be executed if others are issuing print jobs
Using a Queue for Traversing a
Multi-Branch Data Structure
A graph models a network of nodes,
with links connecting nodes
to other nodes in the network
A node in a graph may have several
neighbors
Programmers doing a breadth-first traversal often use a
queue to ensure that nodes closer to the starting point
are visited before nodes that are farther away
You can learn more about graph traversal in Chapter
10
Specification for a Queue Interface
front = 0 size = 0
capacity = 5
front = 0 * size = 0
1
rear = 0 capacity = 5
front = 0 * size = 1
2
rear = 0 capacity = 5
rear = 1 +
front = 0 * size = 32
capacity = 5
rear = 1 +
rear = 2 /
public boolean offer(E item) {
if (size == capacity) {
reallocate();
}
size++;
rear = (rear + 1) % capacity;
theData[rear] = item;
return true;
}
Implementing a Queue Using a
Circular Array (cont.)
q.offer('-');
front = 0 * size = 43
capacity = 5
+
rear = 2 /
public boolean offer(E item) {
rear = 3 - if (size == capacity) {
reallocate();
}
size++;
rear = (rear + 1) % capacity;
theData[rear] = item;
return true;
}
Implementing a Queue Using a
Circular Array (cont.)
q.offer('A');
front = 0 * size = 54
capacity = 5
+
/
public boolean offer(E item) {
rear = 3 - if (size == capacity) {
reallocate();
rear = 4 A }
size++;
rear = (rear + 1) % capacity;
theData[rear] = item;
return true;
}
Implementing a Queue Using a
Circular Array (cont.)
next = q.poll();
front = 0 * size = 5
4
capacity = 5
front = 1 +
/
public E poll() {
- if (size == 0) {
return null
rear = 4 A }
E result = theData[front];
front = (front + 1) % capacity;
size--;
return result;
result = '*' }
Implementing a Queue Using a
Circular Array (cont.)
next = q.poll();
* size = 4
3
capacity = 5
front = 1 +
front = 2 /
public E poll() {
- if (size == 0) {
return null
rear = 4 A }
E result = theData[front];
front = (front + 1) % capacity;
size--;
return result;
result = '+' }
Implementing a Queue Using a
Circular Array (cont.)
q.offer('B');
rear = 0 B
* size = 3
4
capacity = 5
+
front = 2 /
public boolean offer(E item) {
- if (size == capacity) {
reallocate();
rear = 4 A }
size++;
rear = (rear + 1) % capacity;
theData[rear] = item;
return true;
}
Implementing a Queue Using a
Circular Array (cont.)
q.offer('C');
rear = 0 B size = 4
5
capacity = 5
rear = 1 C
+
front = 2 /
public boolean offer(E item) {
- if (size == capacity) {
reallocate();
A }
size++;
rear = (rear + 1) % capacity;
theData[rear] = item;
return true;
}
Implementing a Queue Using a
Circular Array (cont.)
q.offer('D');
B size = 5
capacity = 5
rear = 1 C
+
front = 2 /
public boolean offer(E item) {
- if (size == capacity) {
reallocate();
A }
size++;
rear = (rear + 1) % capacity;
theData[rear] = item;
return true;
}
Implementing a Queue Using a
Circular Array (cont.) theData
q.offer('D'); B
rear = 1 C
+
B size = 5
front = 2 /
capacity = 5
rear = 1 C
+ -
A
front = 2 /
private void reallocate() {
- int newCapacity = 2 * capacity;
E[] newData = (E[])new Object[newCapacity];
A int j = front;
for (int i = 0; i < size; i++) {
newData[i] = theData[j];
j = (j + 1) % capacity;
}
front = 0;
rear = size – 1;
capacity = newCapacity;
theData = newData;
newCapacity = 10 }
Implementing a Queue Using a Circular Array
(cont.) newData
theData
i = 0 B
q.offer('D');
rear = 1 C
+
size = 5 j = 2
front = 2 /
capacity = 5
-
A
i = 0 / B
q.offer('D');
i = 1 rear = 1 C
+
size = 5 j = 2
front = 2 /
capacity = 5 j = 3
-
A
/ q.offer('D'); B
i = 1 - rear = 1 C
+
size = 5
front = 2 /
i = 2 capacity = 5 j = 3
-
A j = 4
/ B j = 0
q.offer('D');
- rear = 1 C
+
size = 5
front = 2 /
i = 2 A
capacity = 5
-
i = 3 A j = 4
/ q.offer('D'); B j = 0
rear = 1 C j = 1
- +
size = 5
front = 2 /
A
capacity = 5
-
i = 3 B A
i = 4
private void reallocate() {
int newCapacity = 2 * capacity;
E[] newData = (E[])new Object[newCapacity];
int j = front;
for (int i = 0; i < size; i++) {
newData[i] = theData[j];
j = (j + 1) % capacity;
}
front = 0;
rear = size – 1;
capacity = newCapacity;
theData = newData;
newCapacity = 10 }
Implementing a Queue Using a Circular Array
(cont.) newData
theData
/ q.offer('D'); B
rear = 1 C j = 1
- +
size = 5 j = 2
front = 2 /
A
capacity = 5
-
B A
i = 4 C
private void reallocate() {
i = 5 int newCapacity = 2 * capacity;
E[] newData = (E[])new Object[newCapacity];
int j = front;
for (int i = 0; i < size; i++) {
newData[i] = theData[j];
j = (j + 1) % capacity;
}
front = 0;
rear = size – 1;
capacity = newCapacity;
theData = newData;
newCapacity = 10 }
Implementing a Queue Using a Circular Array
(cont.) newData
theData
front = 0 / q.offer('D'); B
- rear = 1 C
+
size = 5 j = 2
front = 2 /
A 10
capacity = 5
-
B A
rear = 4 C
private void reallocate() {
i = 5 int newCapacity = 2 * capacity;
E[] newData = (E[])new Object[newCapacity];
int j = front;
for (int i = 0; i < size; i++) {
newData[i] = theData[j];
j = (j + 1) % capacity;
}
front = 0;
rear = size – 1;
capacity = newCapacity;
theData = newData;
newCapacity = 10 }
Implementing a Queue Using a Circular Array
(cont.) theData
front = 0 / q.offer('D');
-
size = 5
6
A 10
capacity = 5
B
rear = 4 C
public boolean offer(E item) {
rear = 5 D
if (size == capacity) {
reallocate();
}
size++;
rear = (rear + 1) % capacity;
theData[rear] = item;
return true;
}
Implementing a Queue Using a
Circular Array (cont.)
Listing 4.4 (ArrayQueue, pages 212-214)
Implementing Class
ArrayQueue<E>.Iter (cont.)
private class Iter implements
• Just as for class Iterator<E> {
ListQueue<E>, we must private int index;
implement the missing private int count = 0;
Queue methods and an inner
class Iter to fully implement public Iter() {
the Queue interface index = front;
}
@Override
public boolean hasNext() {
return count < size;
}
....
Implementing Class
ArrayQueue<E>.Iter (cont.)
private class Iter implements
• Just as for class Iterator<E> {
ListQueue<E>, we must private int index;
implement the missing private int count = 0;
Queue methods and an inner
class Iter to fully implement public Iter() {
the Queue interface index = front;
}
....
Implementing Class
ArrayQueue<E>.Iter (cont.)
private class Iter implements
• Just as for class Iterator<E> {
ListQueue<E>, we must private int index;
implement the missing private int count = 0;
Queue methods and an inner
class Iter to fully implement public Iter() {
the Queue interface index = front;
}
....
Implementing Class
ArrayQueue<E>.Iter (cont.)
private class Iter implements
• Just as for class Iterator<E> {
ListQueue<E>, we must private int index;
implement the missing private int count = 0;
Queue methods and an inner
class Iter to fully implement public Iter() {
the Queue interface index = front;
}
....
Implementing Class
ArrayQueue<E>.Iter (cont.)
private class Iter implements
• Just as for class Iterator<E> {
ListQueue<E>, we must private int index;
implement the missing private int count = 0;
Queue methods and an inner
class Iter to fully implement public Iter() {
the Queue interface index = front;
}
....
Implementing Class
ArrayQueue<E>.Iter (cont.)
@Override
• Just as for class
public E next() {
ListQueue<E>, we must
if (!hasNext()) {
implement the missing
throw new
Queue methods and an inner NoSuchElementException();
class Iter to fully implement }
the Queue interface E returnValue = theData[index];
index = (index + 1) % capacity;
count+;
next() returns the
return returnValue;
element at position index
}
and increments Iter's
fields index and count
@Override
public void remove {
throw new
UnsupportedOperationException();
}
}
Implementing Class
ArrayQueue<E>.Iter (cont.)
@Override
• Just as for class
public E next() {
ListQueue<E>, we must
if (!hasNext()) {
implement the missing
throw new
Queue methods and an inner NoSuchElementException();
class Iter to fully implement }
the Queue interface E returnValue = theData[index];
index = (index + 1) % capacity;
count+;
remove() throws an return returnValue;
exception because
}
removing an item other than
the first item violates the
@Override
queue's contract
public void remove {
throw new
UnsupportedOperationException();
}
}
Comparing the Three Implementations
Computation time
All three implementations are comparable in terms of
computation time
All operations are O(1) regardless of implementation
Section 4.4
Deque Interface
A deque (pronounced "deck") is short for double-ended queue
A double-ended queue allows insertions and removals from
both ends
The Java Collections Framework provides two implementations
of the Deque interface
ArrayDeque
LinkedList