Queue

Download as pdf or txt
Download as pdf or txt
You are on page 1of 7

Queue

A queue is a popular linear data structure that follows the First-In-First-Out (FIFO)
principle. It represents a collection of elements in which the first element added is the first one to
be removed. Queues are used in various scenarios where maintaining order is crucial, such as
scheduling processes, managing network packets, and handling requests. In this comprehensive
guide, we will explore the concept of queues, queue operations (enqueue, dequeue, peek), queue
implementation using arrays and linked lists, circular queues, priority queues, as well as
applications and use cases of queues in the C programming language.
Introduction to Queues
A queue is a linear data structure that follows the First-In-First-Out (FIFO) principle. It
represents a collection of elements in which elements are added at one end (rear) and removed
from the other end (front). Queues are commonly used in scenarios that require maintaining order
and fairness in processing elements.
Queue Operations (Enqueue, Dequeue, Peek)
Queues support the following operations:
Enqueue
The enqueue operation adds an element to the rear (end) of the queue. The newly added element
becomes the last one in the queue.
Dequeue
The dequeue operation removes the element from the front of the queue. The next element in the
queue becomes the new front.
Peek
The peek operation allows you to view the element at the front of the queue without removing it.
It is useful for inspecting the element or performing certain checks.
Queue Implementation (Array-based and Linked List-based)
Queues can be implemented using either arrays or linked lists.
Array-based Queue Implementation
In an array-based queue implementation, an array is used to store the elements. Two indices,
front and rear, keep track of the position of the front and rear elements.
Here's an example of an array-based queue implementation in C:
#include <stdio.h>
#include <stdlib.h>

#define MAX_SIZE 100

typedef struct {
int data[MAX_SIZE];
int front;
int rear;
} Queue;

void enqueue(Queue* queue, int element) {


if (queue->rear == MAX_SIZE - 1) {
printf("Queue Overflow\n");
return;
}
queue->data[++queue->rear] = element;
}

int dequeue(Queue* queue) {


if (queue->front > queue->rear) {
printf("Queue Underflow\n");
return -1; // Return a sentinel value indicating error
}
return queue->data[queue->front++];
}

int peek(Queue* queue) {


if (queue->front > queue->rear) {
printf("Queue is empty\n");
return -1; // Return a sentinel value indicating error
}
return queue->data[queue->front];
}

int main() {
Queue queue;
queue.front = 0;
queue.rear = -1;

enqueue(&queue, 1);
enqueue(&queue, 2);
enqueue(&queue, 3);

printf("Peek: %d\n", peek(&queue));


printf("Dequeue: %d\n", dequeue(&queue));
printf("Peek: %d\n", peek(&queue));

return 0;
}
Explanation:

The code you provided is a queue implementation in C. Let's break it down step by step:

1. Header inclusions:

#include <stdio.h>
#include <stdlib.h>

• stdio.h is a standard library header file that provides input/output functions


like printf used in this code.
• stdlib.h might not be strictly necessary
in this case, but it provides functions for
general purpose memory allocation which may be useful in queue implementations
using dynamic arrays.

2. Defining the Queue data structure:

#define MAX_SIZE 100

typedef struct {
int data[MAX_SIZE];
int front;
int rear;
} Queue;

• MAX_SIZE is defined as a constant with a value of 100. This represents the


maximum number of elements the queue can hold.
• A struct named Queue is defined. It has three integer members:
o data: An array of integers to store the queue elements.
o front: An integer variable that keeps track of the index of the front element in
the queue. Initially, it points to the beginning of the queue (index 0).
o rear: An integer variable that keeps track of the index of the last element in
the queue. Initially, it is set to -1 because an empty queue has no elements.

3. Queue operations:

The code defines three functions to perform enqueue, dequeue, and peek operations on
the queue:

void enqueue(Queue* queue, int element) {


// ...
}

int dequeue(Queue* queue) {


// ...
}

int peek(Queue* queue) {


// ...
}

• enqueue(Queue* queue, int element) :


This function adds a new element to the back of
the queue. It checks if the queue is full (if rear is at the end of the data array). If not,
it increments rear and adds the new element at that index in the data array.
• dequeue(Queue* queue): This function removes and returns the element at the front
of the queue. It checks if the queue is empty (if front is ahead of rear). If not, it
removes the element at the front index and increments front.
• peek(Queue* queue): This function returns the element at the front of the queue
without removing it. It works similarly to dequeue but doesn't modify the front index.

4. Main function:

int main() {
Queue queue;
queue.front = 0;
queue.rear = -1;

enqueue(&queue, 1);
enqueue(&queue, 2);
enqueue(&queue, 3);

printf("Peek: %d\n", peek(&queue));


printf("Dequeue: %d\n", dequeue(&queue));
printf("Peek: %d\n", peek(&queue));

return 0;
}

• The main function creates a Queue object named queue and


initializes front and rear as defined earlier.
• It then calls enqueue to add the elements 1, 2, and 3 to the queue.
• peek is called to see the front element (without removing it), which should be 1.
• dequeue is called to remove and print the front element, which is 1.
• Another peek is called to see the new front element, which should be 2.

This is a basic example of a queue implementation in C. It demonstrates how to use


arrays and functions to manage elements in a First-In-First-Out (FIFO) order.

Linked List-based Queue Implementation

In a linked list-based queue implementation, a linked list is used to store the elements. The front
and rear of the queue are represented by the head and tail of the linked list, respectively.
Here's an example of a linked list-based queue implementation in C:

#include <stdio.h>
#include <stdlib.h>
typedef struct Node {
int data;
struct Node* next;
} Node;

typedef struct {
Node* front;
Node* rear;
} Queue;

void enqueue(Queue* queue, int element) {


Node* newNode = (Node*)malloc(sizeof(Node));
newNode->data = element;
newNode->next = NULL;

if (queue->front == NULL) {
queue->front = newNode;
queue->rear = newNode;
} else {
queue->rear->next = newNode;
queue->rear = newNode;
}
}

int dequeue(Queue* queue) {


if (queue->front == NULL) {
printf("Queue Underflow\n");
return -1; // Return a sentinel value indicating error
}

Node* temp = queue->front;


int data = temp->data;

queue->front = queue->front->next;

if (queue->front == NULL) {
queue->rear = NULL;
}

free(temp);
return data;
}

int peek(Queue* queue) {


if (queue->front == NULL) {
printf("Queue is empty\n");
return -1; // Return a sentinel value indicating error
}
return queue->front->data;
}

int main() {
Queue queue;
queue.front = NULL;
queue.rear = NULL;

enqueue(&queue, 1);
enqueue(&queue, 2);
enqueue(&queue, 3);

printf("Peek: %d\n", peek(&queue));


printf("Dequeue: %d\n", dequeue(&queue));
printf("Peek: %d\n", peek(&queue));

return 0;
}

Explanation:

This code implements a queue using a linked list in C. Here's a breakdown:

1. Header inclusions:

#include <stdio.h>
#include <stdlib.h>

These are the same as the previous program. stdio.h provides input/output functions and stdlib.h
provides memory allocation functions (used for creating nodes).

2. Defining Node and Queue structures:

typedef struct Node {


int data;
struct Node* next;
} Node;

typedef struct {
Node* front;
Node* rear;
} Queue;

• A Node struct is defined. It stores:


o data: The integer value stored in the node.
o next: A pointer to the next node in the linked list.
• A Queue struct is defined. It stores two pointers:
o front: Points to the first node in the queue (the node containing the element to be
dequeued first).
o rear: Points to the last node in the queue (where new elements are enqueued).

3. Queue operations:
void enqueue(Queue* queue, int element) {
// ...
}

int dequeue(Queue* queue) {


// ...
}

int peek(Queue* queue) {


// ...
}
These functions are similar to the previous program but operate on linked list nodes instead of
an array.
• enqueue(Queue* queue, int element): This function creates a new node, assigns the
element to its data field, and sets its next to NULL. If the queue is empty (front is NULL),
both front and rear are set to point to the new node. Otherwise, the next pointer of the
current rear node is set to the new node, and rear is updated to point to the new node.
• dequeue(Queue* queue): This function checks if the queue is empty. If not, it stores a
temporary pointer to the front node, retrieves its data, updates front to point to the next
node, and frees the memory of the dequeued node. If dequeuing the only element, rear is
also set to NULL. It returns the data of the dequeued element.
• peek(Queue* queue): This function checks if the queue is empty. If not, it simply returns
the data of the node pointed to by front (without removing it).

4. Main function:

int main() {
// ...
}

This is similar to the previous program's main function. It creates a Queue object, enqueues
elements, peeks and dequeues them, demonstrating how the queue functions work.

Key differences from the array implementation:


• This program uses linked lists, which are more flexible in terms of size (can grow
dynamically).
• Each node stores the data and a pointer to the next node, forming a chain.
• front and rear pointers track the beginning and end of the queue within the linked list.

You might also like