How to implement a queue using linked list in Python

How to Implement a Queue Using Linked List in Python

Queues are fundamental data structures used in various computing scenarios, from managing tasks in operating systems to handling requests in web servers. In this article, we will explore how to implement a queue using a linked list in Python. We will cover definitions, use cases, and provide actionable code examples. By the end, you will have a solid understanding of how to create a queue from scratch using linked lists.

What is a Queue?

A queue is a linear data structure that follows the First In First Out (FIFO) principle. This means that the first element added to the queue will be the first one to be removed. Queues are widely used in scenarios like:

  • Task Scheduling: Managing processes in operating systems.
  • Print Spooling: Queuing print jobs in printers.
  • Breadth-First Search: Traversing trees and graphs.

Why Use a Linked List for a Queue?

While queues can be implemented using arrays, linked lists offer several advantages:

  • Dynamic Size: Unlike arrays, linked lists can grow and shrink as needed, which can help optimize memory usage.
  • Efficient Insertions/Deletions: Adding or removing elements from the front or back of a linked list is an O(1) operation, while arrays require shifting elements.

Implementing a Queue Using a Linked List

Step 1: Define the Node Class

First, we need to create a Node class that will represent each element in the queue. Each node will contain data and a reference to the next node.

class Node:
    def __init__(self, data):
        self.data = data
        self.next = None

Step 2: Define the Queue Class

Next, we will define the Queue class. This class will maintain references to the front and rear of the queue, along with methods to enqueue, dequeue, and check if the queue is empty.

class Queue:
    def __init__(self):
        self.front = None
        self.rear = None
        self.size = 0

    def is_empty(self):
        return self.size == 0

    def enqueue(self, data):
        new_node = Node(data)
        if self.rear is None:
            self.front = self.rear = new_node
        else:
            self.rear.next = new_node
            self.rear = new_node
        self.size += 1
        print(f"Enqueued: {data}")

    def dequeue(self):
        if self.is_empty():
            print("Queue is empty!")
            return None
        temp = self.front
        self.front = self.front.next
        if self.front is None:
            self.rear = None
        self.size -= 1
        print(f"Dequeued: {temp.data}")
        return temp.data

    def peek(self):
        if self.is_empty():
            print("Queue is empty!")
            return None
        return self.front.data

    def get_size(self):
        return self.size

Step 3: Code Explanation

  • Node Class: This class initializes a node with data and a next pointer.
  • Queue Class:
  • Initialization: Sets the front and rear pointers to None and initializes size to 0.
  • is_empty Method: Checks if the queue is empty.
  • enqueue Method: Adds a new node to the back of the queue. If the queue is empty, it sets both front and rear to the new node. Otherwise, it updates the rear.
  • dequeue Method: Removes the front node from the queue. If the queue becomes empty after this operation, it updates the rear pointer to None.
  • peek Method: Returns the value at the front of the queue without removing it.
  • get_size Method: Returns the current size of the queue.

Step 4: Testing the Queue Implementation

Now let’s test our queue implementation with some simple operations.

if __name__ == "__main__":
    q = Queue()

    q.enqueue(10)
    q.enqueue(20)
    q.enqueue(30)

    print(f"Front element is: {q.peek()}")
    print(f"Queue size is: {q.get_size()}")

    q.dequeue()
    print(f"Front element after dequeue is: {q.peek()}")
    print(f"Queue size after dequeue is: {q.get_size()}")

    q.dequeue()
    q.dequeue()
    q.dequeue()  # This should trigger a message that the queue is empty

Key Output

When you run the above code, you should see output indicating the elements enqueued and dequeued, along with the current size of the queue.

Troubleshooting Common Issues

When implementing queues using linked lists, you may encounter several common issues:

  • Memory Leaks: Ensure that nodes are properly dereferenced to avoid memory leaks.
  • Incorrect Size Reporting: Always update the size attribute appropriately after enqueueing or dequeueing.
  • Null Pointer Exceptions: Handle cases where the queue is empty gracefully.

Conclusion

Implementing a queue using a linked list in Python is a straightforward process that enhances your understanding of data structures. By following the steps outlined in this article, you can create a dynamic and efficient queue that can be adapted for various applications. Whether you are managing tasks in a software application or implementing algorithms, mastering queues will be invaluable in your programming toolkit.

Now, you have the knowledge and tools to implement a queue using a linked list in Python. Go ahead and experiment with the code, tweak it, and see how it can be applied in real-world scenarios!

SR
Syed
Rizwan

About the Author

Syed Rizwan is a Machine Learning Engineer with 5 years of experience in AI, IoT, and Industrial Automation.