Processing math: 100%
Close
Close Window

Show Source |    | About   «  9.11. Implementing Recursion   ::   Contents   ::   9.13. Linked Queues  »

9.12. Queues

9.12.1. Queue Terminology and Implementation

Like the stack, the queue is a list-like structure that provides restricted access to its elements. Queue elements may only be inserted at the back (called an enqueue operation) and removed from the front (called a dequeue operation). Queues operate like standing in line at a movie theater ticket counter. If nobody cheats, then newcomers go to the back of the line. The person at the front of the line is the next to be served. Thus, queues release their elements in order of arrival. In Britain, a line of people is called a “queue”, and getting into line to wait for service is called “queuing up”. Accountants have used queues since long before the existence of computers. They call a queue a “FIFO” list, which stands for “First-In, First-Out”. Here is a sample queue ADT. This section presents two implementations for queues: the array-based queue and the linked queue.

public interface Queue { // Queue class ADT
  // Reinitialize queue
  public void clear();

  // Put element on rear
  public boolean enqueue(Object it);

  // Remove and return element from front
  public Object dequeue();

  // Return front element
  public Object frontValue();

  // Return queue size
  public int length();

  // Return true if the queue is empty
  public boolean isEmpty();
}

9.12.1.1. Array-Based Queues

The array-based queue is somewhat tricky to implement effectively. A simple conversion of the array-based list implementation is not efficient.

1 / 5 Settings
<<<>>>

Assume that there are n elements in the queue. By analogy to the array-based list implementation, we could require that all elements of the queue be stored in the first $n$ positions of the array.

front
rear
  1. 4
listSize
  1. 120
  2. 451
  3. 52
  4. 813
  5. 4
  6. 5
  7. 6
  8. 7
Proficient Saving... Error Saving
Server Error
Resubmit


1 / 9 Settings
<<<>>>

A more efficient implementation can be obtained by relaxing the requirement that all elements of the queue must be in the first $n$ positions of the array. We still require that the queue be stored be in contiguous array positions, but the contents of the queue will be permitted to drift within the array.

Proficient Saving... Error Saving
Server Error
Resubmit


1 / 7 Settings
<<<>>>

This implementation raises a new problem. When elements are removed from the queue, the front index increases.

  1. 0
  2. 1
  3. 2
  4. 173
  5. 34
  6. 305
  7. 46
  8. 7
  9. 8
  10. 9
  1. 3
front
  1. 6
rear
  1. 4
listSize
Proficient Saving... Error Saving
Server Error
Resubmit

9.12.1.2. The Circular Queue

1 / 5 Settings
<<<>>>

The "drifting queue" problem can be solved by pretending that the array is circular and so allow the queue to continue directly from the highest-numbered position in the array to the lowest-numbered position.

Created with Raphaël 2.1.2
0
1
2
3
4
5
6
7
8
9
10
11
Proficient Saving... Error Saving
Server Error
Resubmit


1 / 8 Settings
<<<>>>

There remains one more serious, though subtle, problem to the array-based queue implementation. How can we recognize when the queue is empty or full?

Created with Raphaël 2.1.2
0
1
2
3
4
5
6
7
8
9
10
11
Proficient Saving... Error Saving
Server Error
Resubmit

If the value of front is fixed, then n+1 different values for rear are needed to distinguish among the n+1 states. However, there are only n possible values for rear unless we invent a special case for, say, empty queues. This is an example of the Pigeonhole Principle. The Pigeonhole Principle states that, given n pigeonholes and n+1 pigeons, when all of the pigeons go into the holes we can be sure that at least one hole contains more than one pigeon. In similar manner, we can be sure that two of the n+1 states are indistinguishable by the n relative values of front and rear. We must seek some other way to distinguish full from empty queues.

One obvious solution is to keep an explicit count of the number of elements in the queue, or at least a Boolean variable that indicates whether the queue is empty or not. Another solution is to make the array be of size n+1, and only allow n elements to be stored. Which of these solutions to adopt is purely a matter of the implementor’s taste in such affairs. Our choice here is to use an array of size n+1.

Here is an array-based queue implementation.

class AQueue implements Queue {
  private Object queueArray[]; // Array holding queue elements
  private static final int DEFAULT_SIZE = 10;
  private int maxSize;         // Maximum size of queue
  private int front;           // Index of front element
  private int rear;            // Index of rear element

  // Constructors
  AQueue(int size) {
    maxSize = size + 1;          // One extra space is allocated
    rear = 0; front = 1;
    queueArray = new Object[maxSize];  // Create queueArray
  }
  AQueue() { this(DEFAULT_SIZE); }

  // Reinitialize
  public void clear() { rear = 0; front = 1; }

  // Put "it" in queue
  public boolean enqueue(Object it) {
    if (((rear+2) % maxSize) == front) return false;  // Full
    rear = (rear+1) % maxSize; // Circular increment
    queueArray[rear] = it;
    return true;
  }

  // Remove and return front value
  public Object dequeue() {
    if(length() == 0) return null;
    Object it = queueArray[front];
    front = (front+1) % maxSize; // Circular increment
    return it;
  }

  // Return front value
  public Object frontValue() {
    if (length() == 0) return null;
    return queueArray[front];
  }

  // Return queue size
  public int length() { return ((rear+maxSize) - front + 1) % maxSize; }

  // Check if the queue is empty
  public boolean isEmpty() { return front - rear == 1; }
}

9.12.1.3. Array-based Queue Implementation

1 / 5 Settings
<<<>>>

Member queueArray holds the queue elements...

  • class AQueue implements Queue {
  • private Object queueArray[]; // Array holding queue elements
  • private static final int DEFAULT_SIZE = 10;
  • private int maxSize; // Maximum size of queue
  • private int front; // Index of front element
  • private int rear; // Index of rear element
  • // Constructors
  • AQueue(int size) {
  • maxSize = size + 1; // One extra space is allocated
  • rear = 0; front = 1;
  • queueArray = new Object[maxSize]; // Create queueArray
  • }
  • AQueue() { this(DEFAULT_SIZE); }
Proficient Saving... Error Saving
Server Error
Resubmit

In this implementation, the front of the queue is defined to be toward the lower numbered positions in the array (in the counter-clockwise direction in the circular array), and the rear is defined to be toward the higher-numbered positions. Thus, enqueue increments the rear pointer (modulus maxSize), and dequeue increments the front pointer. Implementation of all member functions is straightforward.

9.12.2. Array-based Dequeue Practice

   «  9.11. Implementing Recursion   ::   Contents   ::   9.13. Linked Queues  »

Close Window