Prepare for your exams

Study with the several resources on Docsity

Earn points to download

Earn points by helping other students or get them with a premium plan

Guidelines and tips

Prepare for your exams

Study with the several resources on Docsity

Earn points to download

Earn points by helping other students or get them with a premium plan

Community

Ask the community

Ask the community for help and clear up your study doubts

University Rankings

Discover the best universities in your country according to Docsity users

Free resources

Our save-the-student-ebooks!

Download our free guides on studying techniques, anxiety management strategies, and thesis advice from Docsity tutors

This is the notes of queue data structures

Typology: Study notes

2022/2023

1 / 18

Download Queue Data structure and more Study notes Computer Programming in PDF only on Docsity! QUEUE 1 QUEUE Created Reviewed Unlike arrays, where insertion and deletion can happen at any end. In Queues, insertion (Enqueue) and deletion (Dequeue) can happen at only one end each. Insertion happens at the rear end and deletion happens at the front end. Queues follow FIFO First in First out structure, i.e. the element added first (Enqueued) will go out of the queue first(Dequeued) Unlike stack, which follows, LIFO, last in first out, and stack where both insertion and deletion happens as one end. For queue insertion(Enqueue) and deletion(Dequeue) happens at opposite ends. Queue Operations Enqueue: Adding a new item to the Queue Data Structure, in other words, enqueuing new item to Stack DS. If the Queue is full, then it is said to be in an overflow condition bool isFull() { // If the rear variable is equal to SIZE - 1 then the queue is full if (rear == SIZE - 1) return true; // Otherwise else return false; } if(isFull() == true) { "Cannot insert element"; return; } // Function to check if the queue is full if(front==-1) { front = 0; } rear = rear+1; queue[rear] = new_element_value; return; The time complexity of this operation is O(1). @October 1, 2023 12:57 PM QUEUE 2 Dequeue: Removing an item from the Queue, i.e. dequeuing an item out. If a Queue is empty then it is said to be in an underflow condition bool isEmpty() { // If both front and rear are -1 then the queue is empty if (front == -1 && rear ==-1) return true; // Otherwise else return false; } if(isEmpty() == true) { "Cannot delete element"; return; } queue[front] = 0; front = front + 1; // if the list is now empty if(front>rear) { front = -1; rear = -1; } return; QUEUE 5 { printf("\nUnderflow Condition Encountered\n"); } // Otherwise we pop the element in the front else { // Print the popped element printf("Dequeued/Popped : %d\n", queue[front]); // Increase the index of the front front++; // Resetting the queue when the last item is popped from the queue if (front > rear) { // Assigning both the front and the rear -1 front = rear = -1; } } } // Function to print the queue void display() { // If the queue is already empty if (rear == -1) { printf("Queue is empty\n"); } // Otherwise else { // A variable to help in iteration through the queue. int i; printf("\nPrinting the Queue :\n"); // Printing all the elements of the queue for (i = front; i <= rear; i++) { printf("%d ", queue[i]); } printf("\n"); } } int main() { // Checking if the queue is empty if (isEmpty()) printf("The queue is empty\n"); // Assigning elements to the queue Enqueue(1); Enqueue(28); Enqueue(5); Enqueue(64); // Printing the first element of the queue peek(); // Printing the size of the queue printf("The size of the queue is %d\n", size()); // Printing the queue display(); // Popping the elements from the queue Dequeue(); Dequeue(); // Printing the size of the queue printf("The size of the queue is %d\n", size()); QUEUE 6 // Printing the queue display(); return 0; } Limitations The method of implementing queue in C using seems good enough. But this method has its limitations. If we look into closely, the available space to store the elements reduces after a few insertion and deletion operations. For example, initially, the queue, of size 5, was empty, then we add three elements to it: 1, 2, and 3. The front variable points to index 0, while the rear variable points to index 2. Now, if we delete two elements the queue will be left with only element 3, and the front, as well as rear, will point to index 2. Adding two more elements to the queue: 4 and5 the queue is now 3, 4, 5, and the front points to index 2, while the rear points to index 4. This means that if we now try adding another element, we will get a message that the queue is full. But the first two indexes, i.e. 0 and 1 are empty. To overcome this limitation, we can make use of circular queues. There different types of queue are:- Simple Queue Circular Queue Priority Queue Double Ended Queue REVERSE A QUEUE Method 1 : Reverse A Queue Using Stack 1. Create a queue and a stack data structure. We’ll use the queue to store the input elements, and the stack to temporarily hold the elements while we reverse the queue. 2. Enqueue all the elements of the original queue into the stack until the queue is empty. 3. Dequeue all the elements from the stack and enqueue them back into the original queue. 4. Repeat steps 2 and 3 until the stack is empty. 5. The original queue will now be reversed. Method 2 : Reverse A Queue Using Recursion 1. Pop the front element from the queue and store it in a variable. 2. Recursively reverse the remaining elements of the queue. 3. Enqueue the front element that was popped in step 1 to the back of the reversed queue. 4. Repeat steps 1 to 3 until all elements have been dequeued and enqueued. 5. The original queue will now be reversed. Algorithm for enqueue(int d) STRUCT NODE* NEW_N NEW_N->DATA = D QUEUE 7 NEW_N->NEXT = NULL IF((FRONT == NULL)&&(REAR == NULL)) FRONT = REAR = NEW_N ELSE REAR->NEXT = NEW_N REAR = NEW_N Algorithm for dequeue() STRUCT NODE *TEMP TEMP = FRONT IF((FRONT == NULL)&&(REAR == NULL)) RETURN ELSE FRONT = FRONT->NEXT FREE(TEMP) print() STRUCT NODE* TEMP IF((FRONT == NULL)&&(REAR == NULL)) RETURN ELSE TEMP = FRONT WHILE(TEMP) RETURN TEMP->DATA TEMP = TEMP->NEXT IMPLEMENTATION OF QUEUE USING LINKED LIST #include <stdio.h> #include <stdlib.h> struct node { int data; struct node* next; };//defining linked list to implement queue struct node *front = NULL; struct node *rear = NULL; void enqueue(int data)//function to insert a node in queue { struct node* new_n; new_n = (struct node*)malloc(sizeof(struct node)); new_n->data = data; QUEUE 10 struct Queue { // Initializing the stacks stack<int> stack1, stack2; // Function to implement enqueue void enqueue(int element) { // Pushing element in the stack stack1.push(element); } // Function to implement dequeue operation int dequeue() { // Condition where the queue is empty if (stack1.empty() && stack2.empty()) { cout << "the queue is empty"; } // Pushing all the elements of stack1 to stack2 if (stack2.empty()) { while (!stack1.empty()) { stack2.push(stack1.top()); stack1.pop(); } } // Popping the top element from the stack int element = stack2.top(); stack2.pop(); return element; } }; Time Complexity: push operation: O(1) It is same as the push operation in the stack. pop operation: O(N). In the worst case we have empty whole of stack 1 into stack 2. Space Complexity: O(N) => N space for storing N elements in the stacks. QUEUE 11 CIRCULAR QUEUE What is Circular Queue? A circular queue is a linear queue that behaves as if the end position of the queue is connected to the start position, forming a circular ring. It is also known as a Ring Buffer. The queue pointers( Front pointer and Rear pointer) will start moving from 0 to N-1 (Maximum size of Queue = N) and after reaching N-1, they come back to 0, again they start from 0,1.., upto N-1 and they continue moving around the Queue round and round 1. Enqueue Operation in Circular Queue The enqueue operation means inserting some element into the queue, with every valid enqueue operation the rear pointer keeps incrementing. Example 1: Insertion of element on empty Circular Queue. If initially, the queue is empty then on the first insertion the front and rear will be set to the initial position and both will point to the same element. Example 2: Insertion of element when already there are some elements in Circular Queue. In such a case, the rear pointer will keep incrementing for each insertion. QUEUE 12 Example 3: Insertion of element when the rear pointer is already at the end of the queue but still there is some space remaining for insertion. If increment of the rear pointer is not possible but there is enough space for insertion, the rear pointer shifts towards 0th index or first position. 2. Dequeue Operation in Circular Queue The dequeue operation means deleting some element from the queue, With every valid dequeue operation, the front pointer keeps incrementing. Example 1: Deletion of the element from non-empty Circular Queue. During deletion, the value corresponding to the front pointer is removed and the front pointer keeps incrementing. Example 2: Deletion of the element, when the front pointer is already at the last position of the non-empty Circular Queue, and further increment of position is not possible. In the circular queue, both pointers can circularly take the positions so, the front pointer will shift to the first position of the circular queue. #include <stdio.h> #include <stdlib.h> #define info(x) printf("%s: %d\n", #x, x); // We are declaring both pointer as global so that each function // can access without passing as the parameter. int front = -1; int rear = -1; int size = -1; // Declaring a pointer that will store the base address of the dynamically allocated array int *circularQueue; // Function to insert the element in the circular queue void enQueue(int element) { // We will only insert the element conditionally // If the front is at 0 and the rear is at the last index of the array it means the array is full // If there is the only a difference of 1 index in front and rear then it also means that the array is full // If (Rear + 1) % N == Front Print " Overflow " if ((front == 0 && rear == size - 1) || (front == rear + 1)) { printf("Queue is FULL"); return; } // If there was no element initially then make the front as well as rear to point to 0th index if (front == -1) { front = 0; rear = 0; } // Else change the rear index // If the rear was at the last index of the array then make it 0 because the rear can take the position in a circular manner // else increment the rear pointer to store the new element else { if (rear == size - 1) rear = 0; else rear = rear + 1; QUEUE 15 front: 1 rear: 3 Deleted Element: 5 front: 2 rear: 3 Queue elements: 1 7 Application of Circular Queue 1. Memory Management in Operating System: In operating systems, processes are stored in a circular process queue wherein the old processes are removed for execution and new processes are added or queued. The unused memory locations post process dequeue are utilized using circular queues which would have not been possible in ordinary queues. 2. Traffic system: In computer controlled traffic systems, circular queues are used to switch on the traffic lights one by one repeatedly as per the time set. 3. CPU Scheduling: Operating systems often maintain a queue of processes that are ready to execute or that are waiting for a particular event to occur. Implementation of circular queue using linked list Circular queues implemented using linked lists are flexible as the size can dynamically adjust. The front and rear pointers are used to manage the linked list, ensuring a circular structure. 1. Enqueueing involves adding a new node at the end of the list, while memory allocation is checked to avoid overflow. In enqueue there will be any of two cases seen while inserting a new node: A. Insertion in an empty list B. Insertion in a non-empty list/ normal insertion A. Insertion in an empty list: check if the list is empty and if true, make the front pointer or head pointer to point to the newly created node. The first node is also the last node of the list. That being the case we make our rear pointer to point to the same block to mark the end of the list.[front = rear = newnode] B. Insertion in a non-empty list/ normal insertion: If the list is not empty, add the new node to the end of the list. We know that the last node is pointed by the rear pointer. make the next pointer of the rear pointer to point to the new node [rear->next = newNode]. On insertion of a new node to the list, the rear pointer becomes the second last node. We make it point to the last node by rear = newNode. Once the new node is added to the list, link the rear pointer point to the front pointer to form the circular queue. Hence, at the end we do rear->next=front; 2. In the dequeue function, we check if the list is empty by verifying if the front pointer is NULL. If it is, we exit. Otherwise, we retrieve the first element using the front pointer and store it in a variable called deleted_item. To remove the frontNode from the list, we update the front pointer to point to the next element or NULL if the list becomes empty. The rear pointer is adjusted accordingly. If the deleted item was the last element, both the front and rear pointers are set to NULL. Finally, we return the deleted item. QUEUE 16 #include<stdio.h> #include<stdlib.h> struct node { int val; struct node *next; // to link the next node }; struct node *front=NULL; struct node *rear=NULL; void enqueue(int item); int dequeue(); int isEmpty(); void enqueue(int item) { struct node *newNode; newNode=(struct node *)malloc(sizeof(struct node)); if(rear->next == NULL){ printf("Memory Overflow\n"); return; } newNode -> val = item; newNode -> next = NULL; if(front == NULL) // checking whether the Queue is empty or not. { front = rear = newNode; } else { rear -> next = newNode; } rear = newNode; rear->next=front; } int dequeue() { struct node *frontNode; int deleted_item; if(isEmpty()) { printf("Queue Underflow\n"); exit(1); } frontNode = front; deleted_item = frontNode -> val; if(front == rear){ front = rear = NULL; } else{ front = front -> next; rear -> next = front; } free(frontNode); return deleted_item; } int isEmpty() { return ( (front == NULL) ? 1 : 0); } void display() { struct node *ptr; if(isEmpty()) { printf("Queue is empty\n."); return; QUEUE 17 } ptr = front; printf("\nElements in the Queue are :"); while( ptr -> next != front ) { printf("%d, ", ptr -> val); ptr = ptr -> next; } printf("%d\n", ptr -> val); } int main() { // Insert elements : 42, 66, 35 enqueue(42); enqueue(66); enqueue(35); display(); // Delete all of them printf("Deleted Element is: %d\n",dequeue()); printf("Deleted Element is: %d\n",dequeue()); printf("Deleted Element is: %d\n",dequeue()); display(); // Insert 10 enqueue(10); display(); // Try deleting an element in an empty circular queue. printf("Deleted Element is: %d\n",dequeue()); return 0; } PRIORITY QUEUE It is a special type of queue in which deletion and insertion perform on the basis of priority of data element. There can be min priority queue and max priority queue. Priority Queue can be implemented by three data Structure:- Array Linked List Heap More About Priority Queue in C Programming Priority Queue is the special case of Queue. In priority queue deletion and insertion base on priority of the data elements In Queue there is priority for each element. There is two types of Priority Queue min priority queue and max priority queue.