How to Implement Queue in C#

To implement a queue in C#, start by defining a queue class and declaring a suitable data structure like an array or linked list. Implement methods for enqueueing, dequeuing, and optionally peeking at elements. Handle edge cases like an empty queue or limited capacity, and optimize performance by choosing efficient data structures.

What is a Queue in C#?

A queue can be best described as a linear data structure that follows the principle of FIFO (First In, First Out). In simpler terms, the first element added to the queue is the first one to be removed. This ordering principle resembles the way people line up in queues in real-world scenarios, such as waiting in line at a ticket counter or a checkout lane in a supermarket.

The queue data structure comprises two primary operations: Enqueue and Dequeue.

  • Enqueue: This operation adds an element to the rear or end of the queue.
  • Dequeue: This operation removes and returns the element at the front or beginning of the queue.

Using Built-in Queue Collection

C# provides a built-in Queue collection in the System.Collections namespace, offering convenient methods for managing queues without having to implement the data structure from scratch. Let’s explore how to utilize this collection effectively:

  • Initializing a Queue: To create a new Queue instance, simply instantiate it using the Queue<T> class, where T represents the type of elements to be stored in the queue.
Queue<int> myQueue = new Queue<int>();

This code initializes a new queue of integers named myQueue.

  • Enqueuing Elements: Adding elements to the queue is achieved using the Enqueue() method, which inserts the specified element at the rear of the queue.
myQueue.Enqueue(10);

This line adds the integer 10 to the rear of the queue.

  • Dequeuing Elements: Removing elements from the queue is done with the Dequeue() method, which removes and returns the element at the front of the queue.
int dequeuedItem = myQueue.Dequeue();

This code removes and returns the item at the front of the queue.

  • Peek Operation: The Peek() method allows you to retrieve the element at the front of the queue without removing it.
int frontItem = myQueue.Peek();

This retrieves the item at the front of the queue without removing it.

  • Checking if Queue is Empty: You can use the Count property or the IsEmpty() method to determine whether the queue is empty.
bool isEmpty = myQueue.Count == 0;

This condition checks if the queue is empty by verifying its count.

  • Accessing Queue Elements: Iterating through the elements of the queue can be achieved using loops or by converting the queue to an array.
foreach (var item in myQueue)
{
    Console.WriteLine(item);
}

This loop iterates through each element in the queue, printing its value.

Implementation Of Queue

While the built-in Queue collection in C# is convenient, there may be scenarios where you need more control or want to gain a deeper understanding of how queues work internally. In such cases, implementing the queue with data structure can be beneficial.

Queues can be implemented using various underlying data structures such as arrays, linked lists or even specialized implementations like priority queues.

Implementation of Queue using Array in C#

  • Create a class with an array, front, rear, and capacity.
  • Implement methods for Enqueue, Dequeue, Peek, and IsEmpty.
  • Enqueue: Check if queue is full, then add item to rear.
  • Dequeue: Check if queue is empty, then remove item from front.
  • Peek: Return item at front without removing it.
  • IsEmpty: Return true if front is -1.
public class ArrayQueue
{
    private int[] array;
    private int front;
    private int rear;
    private int capacity;

    public ArrayQueue(int size)
    {
        capacity = size;
        array = new int[size];
        front = rear = -1;
    }

    public void Enqueue(int item)
    {
        if (rear == capacity - 1)
        {
            Console.WriteLine("Queue is full");
            return;
        }
        else if (front == -1)
        {
            front = 0;
        }

        array[++rear] = item;
    }

    public int Dequeue()
    {
        if (front == -1)
        {
            Console.WriteLine("Queue is empty");
            return -1;
        }

        int item = array[front];

        if (front == rear)
        {
            front = rear = -1;
        }
        else
        {
            front++;
        }

        return item;
    }

    public int Peek()
    {
        if (front == -1)
        {
            Console.WriteLine("Queue is empty");
            return -1;
        }

        return array[front];
    }

    public bool IsEmpty()
    {
        return front == -1;
    }
}

Explanation:

  • In the ArrayQueue class, we have an array to store the elements, along with front and rear pointers to keep track of the elements.
  • The Enqueue method adds an item to the rear of the queue, and the Dequeue method removes and returns the item from the front.
  • The Peek method returns the item at the front without removing it, and the IsEmpty method checks if the queue is empty.

Implementation of Queue using Linked List in C#

  • Define a class with a Node class containing data and next pointer.
  • Implement methods for Enqueue, Dequeue, Peek, and IsEmpty.
  • Enqueue: Add a new node at the rear of the queue.
  • Dequeue: Remove the node from the front of the queue.
  • Peek: Return the data of the node at the front.
  • IsEmpty: Return true if the front pointer is null.
public class Node
{
    public int Data;
    public Node Next;

    public Node(int data)
    {
        Data = data;
        Next = null;
    }
}

public class LinkedListQueue
{
    private Node front;
    private Node rear;

    public LinkedListQueue()
    {
        front = rear = null;
    }

    public void Enqueue(int item)
    {
        Node newNode = new Node(item);

        if (rear == null)
        {
            front = rear = newNode;
            return;
        }

        rear.Next = newNode;
        rear = newNode;
    }

    public int Dequeue()
    {
        if (front == null)
        {
            Console.WriteLine("Queue is empty");
            return -1;
        }

        int item = front.Data;
        front = front.Next;

        if (front == null)
        {
            rear = null;
        }

        return item;
    }

    public int Peek()
    {
        if (front == null)
        {
            Console.WriteLine("Queue is empty");
            return -1;
        }

        return front.Data;
    }

    public bool IsEmpty()
    {
        return front == null;
    }
}

Explanation:

  • In the LinkedListQueue class, we use a linked list to implement the queue.
  • The Enqueue method adds a new node with the given item to the rear of the queue.
  • The Dequeue method removes and returns the item from the front of the queue by updating the front pointer.
  • The Peek method returns the item at the front without removing it, and the IsEmpty method checks if the queue is empty.

Handling Edge Cases

Queue Overflow

Queue overflow occurs when attempting to enqueue an element into a queue that has reached its maximum capacity. In the array-based implementation, this happens when the rear pointer reaches the end of the array. To handle this, we need to check if the queue is full before enqueuing an element. If the queue is full, we display an error message indicating that the queue is full and cannot accept any more elements.

public class ArrayQueue
{
    private int[] array;
    private int front;
    private int rear;
    private int capacity;

    public ArrayQueue(int size)
    {
        capacity = size;
        array = new int[size];
        front = rear = -1;
    }

    public void Enqueue(int item)
    {
        if (rear == capacity - 1)
        {
            Console.WriteLine("Queue Overflow: Cannot add item " + item + ". Queue is full.");
            return;
        }
        else if (front == -1)
        {
            front = 0;
        }

        array[++rear] = item;
        Console.WriteLine("Item " + item + " enqueued successfully.");
    }
}

Explanation:

  • In the Enqueue method, we check if the rear pointer has reached the maximum capacity of the queue (capacity - 1). If so, we display a queue overflow message indicating that the queue is full and cannot accept any more elements.
Queue Underflow

Queue underflow occurs when attempting to dequeue an element from an empty queue. In the array-based implementation, this occurs when the front pointer is -1, indicating that the queue is empty. To handle this, we need to check if the queue is empty before dequeuing an element. If the queue is empty, we display an error message indicating that the queue is empty and there are no elements to dequeue.

public class ArrayQueue
{
    private int[] array;
    private int front;
    private int rear;
    private int capacity;

    public ArrayQueue(int size)
    {
        capacity = size;
        array = new int[size];
        front = rear = -1;
    }

    public int Dequeue()
    {
        if (front == -1)
        {
            Console.WriteLine("Queue Underflow: Cannot dequeue from an empty queue.");
            return -1;
        }

        int item = array[front];

        if (front == rear)
        {
            front = rear = -1;
        }
        else
        {
            front++;
        }

        Console.WriteLine("Item " + item + " dequeued successfully.");
        return item;
    }
}

Explanation:

  • In the Dequeue method, we check if the front pointer is -1, indicating an empty queue. If so, we display a queue underflow message indicating that the queue is empty and there are no elements to dequeue.

Importance of Queues in Computer Science

Queues are essential in various computing scenarios due to their efficient and orderly nature. Their significance can be understood through several key points:

  1. Task Scheduling: Queues are widely used in task scheduling algorithms where tasks or processes are queued up and executed based on their arrival time or priority. This ensures fairness and optimizes resource utilization in systems.
  2. Buffering and Data Processing: In networking and operating systems, queues are instrumental in managing data packets and system tasks. They act as buffers, temporarily holding data or requests until they can be processed or transmitted.
  3. Multi-threading and Parallel Processing: Queues facilitate communication and synchronization between threads or processes in concurrent programming paradigms. They enable safe and orderly sharing of data and resources among multiple threads.
  4. Event Handling and Message Passing: Queues are used extensively in event-driven programming and message passing systems. Events or messages are queued up and processed sequentially, ensuring that they are handled in the order they were received.

We provide insightful content and resources to empower developers on their coding journey. If you found this content helpful, be sure to explore more of our materials for in-depth insights into various Programming Concepts.

Also check out(Our Data Structures Series in C#):

Stay tuned for future articles and tutorials that illustrate complex topics, helping you become a more proficient and confident developer.

Share your love