Skip to main content

Section 29.3 Queue ADT

Whereas a stack provides a last in, first out (LIFO) collection, a queue provides a first in, first out (FIFO) collection. In a queue, the first item added to the collection is the first one that will be removed. The second item added will be the second one removed, and so on. Think of a line of people waiting for something. The first person to get in line is the first person to be served and leave the line.
The first in, first out property of a queue lends itself to more straight forward uses than stacks. We pretty much only need a queue if we want to add things to a collection and then remove them in the same order that they were added.
Like a stack, a queue does not imply any particular implementation. The only real requirement is that we can efficiently add items to one end of the collection (the back of the queue) and remove items from the other end (the front of the queue).
In a linked list based implementation of a queue, to efficiently add items to the back and remove them from the front, we have two choices:
  • Use a doubly linked list with a tail pointer, so we can efficiently add or remove at both the head and tail of the list. In this case we can call either end the β€œfront” and the other the β€œback”.
  • Use a singly linked list with a tail pointer. In this case, the only thing that we can efficiently do at the tail of the list is to add values. Values can only be efficiently removed at the head. So in this implementation, we have to define the β€œfront” of the queue to be at the head of the list, and the β€œback” of the queue to be at the tail of the list.
This animation shows a queue implemented with a singly linked list with a tail pointer.

Activity 29.3.1. Queue Implemented with a Linked List.

A queue of integers has been implemented using a singly linked list. Use the interactive below to experiment with enqueueing and dequeueing items from the queue.

Instructions.

When the > button is highlighted, an animation is prepared to run. Use the Step button to step through the animation one step at a time.
When no animation is in progress, you can use the data controls to start a new animation.
You can click and drag on the animation area to pan around. Use the Ctrl + mouse wheel (or touchpad scroll gesture) while over the animation area to zoom in and out.
  • Core Controls.
    • << : Skip to the beginning of the animation.
    • < : Step backwards one step in the animation.
    • > : Step forward one step in the animation.
    • >> : Skip to the end of the animation.
    • Auto Step Speed Set to anything other than off to automatically step through the animation at the selected speed.
    • Zoom Set the zoom level. You can also use Ctrl + Mouse Wheel to zoom in and out.
  • Data Controls.
    • Value Use this area to enter a value when inserting, finding, deleting, etc...
Because this behavior is identical to that of a linked list, just with different terminology, we can easily use an existing linked list implementation (as long as that implementation provides the necessary operations) to create a queue.
class IntQueue {
private:
    list<int> list;  // Underlying linked list to store queue elements
public:
    void enqueue(const int& item) {
        list.push_back(item);  // Add item to the back of the list
    }
    int dequeue() {
        if (list.empty()) {
            throw std::out_of_range("Queue is empty");
        }
        int frontItem = list.front();  // Get the item at the front of the list
        list.pop_front();              // Remove the item from the front of the list
        return frontItem;              // Return the dequeued item
    }
    bool isEmpty() const {
        return list.empty();  // Check if the queue is empty
    }
};
As when we examined implementing a stack using a list (or a vector), using an existing list implementation means the queue class is not responsible for managing the memory of the underlying data structure. It is just responsible for providing the queue interface.

Checkpoint 29.3.1.

What would be an issue with using a vector to implement a queue and treating the beginning of the vector (index 0) as the front of the queue?
  • dequeue would be \(O(n)\)
  • enqueue would be \(O(n)\)
  • enqueue and dequeue would be \(O(n)\)
You have attempted of activities on this page.