C Programming Data Structures Queue And Circular Queue Complete Guide
Understanding the Core Concepts of C Programming data structures Queue and Circular Queue
Data Structures: Queue and Circular Queue in C Programming
Queue: A linear data structure (DS) that follows the First In, First Out (FIFO) principle. This implies that elements are added at one end, known as the back or rear, and removed from the other end, known as the front.
Circular Queue: An advanced version of a queue where the last position is connected back to the first position to form a circle. This data structure overcomes the limitation of regular queues by utilizing space more efficiently.
Structure of a Queue
- Front: Points to the element that will be dequeued first.
- Rear: Points to the element that was recently enqueued.
- Enqueue Operation: Adding an element to the rear.
- Dequeue Operation: Removing an element from the front.
Basic Operations in a Queue
- Initialize Queue: Set
front
andrear
to-1
. - Enqueue: Check if the queue is full (if
(rear + 1) % SIZE == front
). If not, incrementrear
and insert the element. - Dequeue: Check if the queue is empty (if
front == -1
). If not, incrementfront
and remove the element. - Peek: Retrieve the value at the front of the queue without removing it.
- IsEmpty: Check if the queue has no elements.
- IsFull: Check if the queue cannot accept any more elements.
Queue Implementation in C Using an array, a queue can be implemented as follows:
#include <stdio.h>
#define SIZE 10
void initialize(int* front, int* rear) {
*front = *rear = -1;
}
int isFull(int rear) {
return (rear + 1) % SIZE == front;
}
int isEmpty(int front) {
return front == -1;
}
void enqueue(int queue[], int* rear, int* front, int item) {
if(isFull(*rear)) {
printf("Queue Overflow\n");
return;
}
if(isEmpty(*front))
*front = 0;
*rear = (*rear + 1) % SIZE;
queue[*rear] = item;
}
int dequeue(int queue[], int* front, int* rear) {
int item;
if(isEmpty(*front)) {
printf("Queue Underflow\n");
return -1;
}
item = queue[*front];
if(*front == *rear)
*front = *rear = -1; // Reset queue to initial state
else
*front = (*front + 1) % SIZE;
return item;
}
void peek(int queue[], int front) {
if(isEmpty(front)) {
printf("Queue Empty\n");
} else {
printf("%d\n", queue[front]);
}
}
Advantages
- Simple and efficient.
- Used in scenarios like breadth-first search.
- Ideal for operations requiring ordering of elements.
Disadvantages
- Fixed size due to array implementation.
- Waste of space when deque operations occur frequently.
- Dequeue operation requires shifting elements.
Structure of a Circular Queue
A circular queue avoids the issue of wasted space by forming a logical loop between the rear and front of the queue. This makes the use of space more efficient and prevents the requirement of shifting elements during deque operations.
Basic Operations in a Circular Queue Circular Queue operations include all standard queue operations with additional circular behavior rules:
- Initialize Circular Queue: Similar to Queue initialization.
- Enqueue: Add an element at
rear
, incrementingrear
in a circular manner. - Dequeue: Remove an element from
front
, and incrementfront
circularly. - Peek: Similar to Queue peek.
- IsEmpty: Similar to Queue isEmpty.
- IsFull: Checks
(rear + 1) % SIZE == front
.
Circular Queue in C Using an array to create a circular queue:
#include <stdio.h>
#define SIZE 10
struct CircularQueue {
int items[SIZE];
int front;
int rear;
};
int isFull(struct CircularQueue* cq) {
return ((cq->rear + 1) % SIZE == cq->front);
}
int isEmpty(struct CircularQueue* cq) {
return (cq->front == -1);
}
void enqueue(struct CircularQueue* cq, int item) {
if(isFull(cq)) {
printf("Queue Overflow\n");
return;
}
if(cq->front == -1)
cq->front = 0;
cq->rear = (cq->rear + 1) % SIZE;
cq->items[cq->rear] = item;
printf("Inserted -> %d\n", item);
}
int dequeue(struct CircularQueue* cq) {
int item;
if(isEmpty(cq)) {
printf("Queue Underflow\n");
return -1;
}
item = cq->items[cq->front];
if(cq->front == cq->rear) { // Last remaining element
cq->front = cq->rear = -1;
} else {
cq->front = (cq->front + 1) % SIZE;
}
printf("Deleted -> %d\n", item);
return item;
}
void peek(struct CircularQueue* cq) {
if(isEmpty(cq)) {
printf("Queue Empty\n");
} else {
printf("\nFront item: %d\n", cq->items[cq->front]);
}
}
int main() {
struct CircularQueue cq;
cq.front = cq.rear = -1;
enqueue(&cq, 1);
enqueue(&cq, 2);
enqueue(&cq, 3);
peek(&cq);
dequeue(&cq);
dequeue(&cq);
dequeue(&cq);
enqueue(&cq, 10);
peek(&cq);
return 0;
}
Advantages
- Efficient space usage.
- No need to shift elements during the dequeue process.
- Operations are constant time, O(1).
Disadvantages
- Complex compared to simple queue implementations using arrays or linked lists.
- Fixed size.
- Requires careful handling of indices to avoid overflow/underflow conditions.
Comparison
- Queue: Linear and simpler, but may waste space as front moves up after dequeuing.
- Circular Queue: Logical looping around the queue, saves space but is more complex.
Applications
Both queues and circular queues find applications in various fields such as:
- Operating Systems: For managing queues of processes.
- Networks: In scheduling packets for transmission.
- Computer Hardware: Buffer management systems often use them.
- Real-time Systems: To handle events by their arrival order.
In conclusion, while queues provide the fundamental FIFO behavior, circular queues offer enhanced memory usage capabilities essential for many dynamic applications.
Online Code run
Step-by-Step Guide: How to Implement C Programming data structures Queue and Circular Queue
Example 1: Implementing a Queue
A queue is a linear data structure that follows the First-In-First-Out (FIFO) principle. This means the first element added to the queue will be the first one to be removed.
Step-by-Step Implementation:
Define the Queue Structure:
- We need an array to store the elements.
- Two integers,
front
andrear
, to keep track of the positions of the front and rear elements in the queue.
Initialize the Queue:
- Set
front
andrear
to-1
.
- Set
Enqueue Operation:
- Add an element to the rear of the queue.
- If it is the first element, set both
front
andrear
to0
. - Otherwise, increment
rear
.
Dequeue Operation:
- Remove an element from the front of the queue.
- If the front is the same as the rear, set both
front
andrear
to-1
because the queue would be empty after this operation. - Otherwise, increment
front
.
Check if Queue is Empty:
- Simply check if
front
is-1
.
- Simply check if
Check if Queue is Full:
- Check if
rear
is one position behindfront
.
- Check if
Display Queue Elements:
- Loop through the array starting from
front
torear
.
- Loop through the array starting from
Here is the implementation:
#include <stdio.h>
#include <stdbool.h>
#define MAX 10
int queue[MAX];
int front = -1, rear = -1;
// Function to check if the queue is full
bool isFull() {
return rear == MAX - 1;
}
// Function to check if the queue is empty
bool isEmpty() {
return front == -1 && rear == -1;
}
// Function to add an element to the queue
void enqueue(int value) {
if (isFull()) {
printf("Queue is Full!\n");
} else if (isEmpty()) {
front = rear = 0;
} else {
rear++;
}
queue[rear] = value;
printf("%d Enqueued\n", value);
}
// Function to remove an element from the queue
void dequeue() {
if (isEmpty()) {
printf("Queue is Empty!\n");
} else if (front == rear) {
printf("%d Dequeued\n", queue[front]);
front = rear = -1;
} else {
printf("%d Dequeued\n", queue[front]);
front++;
}
}
// Function to display the queue elements
void displayQueue() {
if (isEmpty()) {
printf("Queue is Empty!\n");
} else {
printf("Queue elements are: ");
for (int i = front; i <= rear; i++) {
printf("%d ", queue[i]);
}
printf("\n");
}
}
int main() {
int choice, value;
while (1) {
printf("\nQueue Operations:\n");
printf("1. Enqueue\n");
printf("2. Dequeue\n");
printf("3. Display Queue\n");
printf("4. Exit\n");
printf("Enter your choice: ");
scanf("%d", &choice);
switch (choice) {
case 1:
printf("Enter the value to Enqueue: ");
scanf("%d", &value);
enqueue(value);
break;
case 2:
dequeue();
break;
case 3:
displayQueue();
break;
case 4:
exit(0);
default:
printf("Invalid Choice! Try again.\n");
}
}
return 0;
}
Explanation:
- When the program starts,
front
andrear
are initialized to-1
, indicating that the queue is empty. - The
enqueue
function checks if the queue is full before adding a new value. It adjusts thefront
andrear
indices appropriately. - The
dequeue
function checks if the queue is empty before removing a value. It handles the scenario where the last element is removed and resetsfront
andrear
. - The
displayQueue
function loops fromfront
torear
and prints all the elements currently in the queue.
Example 2: Implementing a Circular Queue
A circular queue is a variation of the queue that is circular in nature, which means that the tail's next reference points back to the head, forming a circle.
Step-by-Step Implementation:
Define the Circular Queue Structure:
- Use an array to store the elements.
- Maintain
front
andrear
pointers. - Ensure that space at index
0
is always unused when the queue is full.
Initialize the Circular Queue:
- Set
front
andrear
to0
.
- Set
Enqueue Operation:
- Add an element to the rear of the queue.
- Use
(rear + 1) % MAX
to get the next position for the rear. - Check if the queue is full before adding an element.
Dequeue Operation:
- Remove an element from the front of the queue.
- Use
(front + 1) % MAX
to get the next position for the front. - Check if the queue is empty before removing an element.
Check if Circular Queue is Empty:
- The queue is empty if
front
is equal torear
.
- The queue is empty if
Check if Circular Queue is Full:
- The queue is full if
(rear + 1) % MAX
equalsfront
.
- The queue is full if
Display Queue Elements:
- Loop through the array starting from
front
torear
.
- Loop through the array starting from
Here is the implementation:
Top 10 Interview Questions & Answers on C Programming data structures Queue and Circular Queue
1. What is a Queue in Data Structures?
Answer: A queue is a linear data structure that follows the First In, First Out (FIFO) principle. This means that elements are added at the back (rear) and removed from the front. Common operations include enqueue (adding an element), dequeue (removing an element), and checking if the queue is empty or full.
2. How do you implement a Queue using Arrays in C?
Answer: To implement a queue using arrays in C, you typically define an array and two integers, front
and rear
, to keep track of the positions of the first and last elements in the queue. Here’s a simple implementation:
#include <stdio.h>
#define SIZE 10
int queue[SIZE], front = -1, rear = -1;
void enqueue(int value) {
if(rear == SIZE - 1)
printf("Queue Overflow\n");
else {
if(front == -1)
front = 0;
rear++;
queue[rear] = value;
}
}
void dequeue() {
if(front == -1 || front > rear) {
printf("Queue Underflow\n");
return;
} else {
printf("\n Deleted Element : %d\n", queue[front]);
front++;
}
}
int main() {
enqueue(10);
enqueue(20);
dequeue();
enqueue(30);
return 0;
}
3. What are the advantages of using a Queue in C?
Answer: The advantages of using a queue in C include:
- Order Preservation: Maintains the order of elements as they enter.
- Efficient for FIFO operations: Dequeue and enqueue operations can be implemented in constant time, O(1).
- Simplicity: Easy to understand and implement using arrays or linked lists.
4. What is a Circular Queue? How does it solve the problem of wasted space in a regular Queue?
Answer: A circular queue overcomes the drawbacks of the normal array-based queue by connecting the end of the queue back to the front, making it more space-efficient.
- Problem with Regular Queue: The regular queue implementation leads to "wasted space" once the rear reaches the maximum index of the array, even though the front has space before it.
- Circular Queue Solution: The circular queue reuses the space of the array. When rear reaches the last element, the next element is placed at index 0 (if the front isn’t at index 0).
5. How do you implement a Circular Queue using Arrays in C?
Answer: Implementing a circular queue using arrays involves similar logic but includes modulo operation for wrapping around.
#include <stdio.h>
#define SIZE 10
int queue[SIZE], front = -1, rear = -1;
int isFull() {
if((front == rear + 1) || (front == 0 && rear == SIZE - 1))
return 1;
return 0;
}
int isEmpty() {
if(front == -1)
return 1;
return 0;
}
void enqueue(int element) {
if(isFull())
printf("Queue is full\n");
else {
if(front == -1) front = 0;
rear = (rear + 1) % SIZE;
queue[rear] = element;
}
}
void dequeue() {
if(isEmpty())
printf("\nQueue is empty\n");
else {
printf("\nDeleted Element : %d\n", queue[front]);
if(front == rear) {
front = -1;
rear = -1;
} else {
front = (front + 1) % SIZE;
}
}
}
int main() {
enqueue(10);
enqueue(20);
dequeue();
enqueue(30);
return 0;
}
6. Can you explain the difference between a Queue and a Circular Queue?
Answer: While both queues and circular queues adhere to the FIFO principle:
- Queue: Uses a linear array, where once elements are removed, no elements can be added until spaces are freed up from the front. This results in space wastage when
front
is not equal to 0 butrear
reaches the end of the array. - Circular Queue: Reuses the space of the array to prevent wastage. The insertion happens at the start when the rear becomes equal to the array size.
7. What are the operations supported by a Circular Queue?
Answer: Operations supported by a circular queue in C include:
- Enqueue: Adding an element to the rear of the queue.
- Dequeue: Removing an element from the front of the queue.
- IsFull: Checking whether the queue is full.
- IsEmpty: Checking whether the queue is empty.
- Peek/Front: Accessing the front element without removing it.
- Size: Calculating the number of elements in the queue.
8. How do you handle overflow and underflow in a Circular Queue?
Answer: In a circular queue:
- Overflow: Occurs when
(rear + 1) % SIZE == front
. Use theisFull()
function to check for overflow. - Underflow: Happens when
front == -1
. Use theisEmpty()
function to check for underflow.
9. Can you provide an example of a Circular Queue Application?
Answer: Applications of circular queues include:
- CPU scheduling: For processes waiting to access CPU resources.
- Memory Management: For memory buffers and input/output buffers.
- Real-time systems: Where tasks must be handled in the order they arrive.
- Traffic lights: Managing the queue of waiting cars efficiently.
10. How do you implement a Queue using Linked Lists in C?
Answer: Using linked lists, the queue has no upper limit based on system memory. Each node points to the next, avoiding the limitation of fixed-size arrays.
Login to post a comment.