Data Structures and Algorithms using Python
Copyright © 2023 by Rance Necaise
Table Of Contents
Data Structures and Algorithms using Python
Copyright © 2023
by Rance Necaise

8.2 Queue Implementation

Since the queue data structure is simply a specialized list, it is commonly implemented using some type of list structure. There are three common approaches to implementing a queue: using a vector, a linked list, or an array. In the following sections, we examine and compare these three approaches.

Python List Implementation

The simplest way to implement the Queue ADT is to use Python's list. It provides the necessary routines for adding and removing items at the respective ends. By applying these routines, we can remove items from the front of the list and append new items to the end. To use a list for the Queue ADT, the constructor must define a single data field to store the list that is initially empty:

class Queue :
  def __init__(self) :
    self._qList = list()

Figure 8.2.1 illustrates an instance of the class for the queue from Figure 8.1.1.

Figure 8.2.1: An instance of the Queue ADT implemented using a Python list.

We can test for an empty queue and obtain the size of the queue by examining the length of the list:

def isEmpty(self) :
  return len(self._qList) == 0

def __len__(self) :
  return len(self._qList)

To enqueue an item, we simply append the item to the end of the list:

def enqueue(self, item) :
  self._qList.append(item)

The dequeue operation can be implemented by popping and returning the item in the first element of the list:

def dequeue(self) :
  assert not self.isEmpty(), "Can not dequeue from an empty queue."
  return self._qList.pop(0)

Before attempting to remove an item from the list, we must ensure the queue is not empty. Remember, the queue definition prohibits the use of the dequeue operation on an empty queue. Thus, to enforce this, we must first assert the queue is not empty and raise an exception, when the operation is attempted on an empty queue. The complete Python list-based implementation is provided in the listing below.

Program Listing
Program: pylistqueue.py
  1. # Implementation of the Queue ADT using a Python list.
  2. class Queue :
  3.    # Creates an empty queue.
  4.   def __init__(self) :
  5.     self._qList = list()
  6.    
  7.    # Returns True if the queue is empty.
  8.   def isEmpty(self) :
  9.     return len(self._qList) == 0
  10.    
  11.    # Returns the number of items in the queue.
  12.   def __len__(self) :
  13.     return len(self._qList)
  14.    
  15.    # Adds the given item to the queue.
  16.   def enqueue(self, item) :
  17.     self._qList.append(item)
  18.    
  19.    # Removes and returns the first item in the queue.
  20.   def dequeue(self):
  21.     assert not self.isEmpty(), "Can not dequeue from an empty queue."
  22.     return self._qList.pop(0)

Since we use list operations to implement the individual queue operations, we need only recall the worst case times for the Python list operations. The size and empty condition operations only require O(1) time. The enqueue operation requires O(n) time in the worst case since the list may need to expand to accommodate the new item. When used in sequence, the enqueue operation has an amortized cost of O(1). The dequeue operation also requires O(n) time since the underlying array used to implement the Python list may need to shrink when an item is removed. In addition, when an item is removed from the front of the list, the following items have to be shifted forward, which requires linear time no matter if an expansion occurs or not. Table 8.2.1 provides a summary of the worst case time-complexities for a queue implemented using a Python list.

OperationWorst Case
q = Queue() O(1)
len(q) O(1)
q.isEmpty() O(1)
q.enqueue(item) O(n)
x = q.dequeue() O(n)
Table 8.2.1: Time-complexities for the Queue implemented using a Python list.

Linked List Implementation

A major disadvantage in using a Python list to implement the Queue ADT is the expense of the enqueue and dequeue operations. A better solution is to use a linked list consisting of both head and tail references. Adding the tail reference allows for quick append operations that otherwise would require a complete traversal to find the end of the list. Figure 8.2.2 illustrates a sample linked list with the two external references.

Figure 8.2.2: An instance of the Queue ADT implemented as a linked list.

The complete implementation of the Queue ADT using a linked list with a tail reference is provided in the listing below. Remember, the individual nodes in the list contain the individual items in the queue. When dequeuing an item, we must unlink the node from the list but return the item stored in that node and not the node itself. An evaluation of the time-complexities is left as an exercise.

Program Listing
Program: llistqueue.py
  1. # Implementation of the Queue ADT using a linked list.
  2. class Queue :
  3.     # Creates an empty queue.
  4.   def __init__(self) :
  5.     self._qhead = None
  6.     self._qtail = None
  7.     self._count = 0
  8.    
  9.    # Returns True if the queue is empty.
  10.   def isEmpty(self) :
  11.     return self._qhead is None
  12.    
  13.    # Returns the number of items in the queue.
  14.   def __len__(self) :
  15.     return self._count
  16.    
  17.    # Adds the given item to the queue.
  18.   def enqueue(self, item) :
  19.     node = QueueNode(item)
  20.     if self.isEmpty() :
  21.       self._qhead = node
  22.     else :
  23.       self._qtail.next = node      
  24.     self._qtail = node
  25.     self._count += 1
  26.    
  27.    # Removes and returns the first item in the queue.
  28.   def dequeue(self) :
  29.     assert not self.isEmpty(), "Cannot dequeue from an empty queue."
  30.     node = self._qhead
  31.     if self._qhead is self._qtail :
  32.       self._qtail = None
  33.      
  34.     self._qhead = self._qhead.next
  35.     self._count -= 1
  36.     return node.item
  37.  
  38. # Private storage class for creating the linked list nodes.
  39. class QueueNode :
  40.   def __init__(self, item) :
  41.     self.item = item
  42.     self.next = None
Page last modified on August 20, 2023, at 02:18 PM