The Ins & Outs of Queues in C++
By: Aman Kumar Goyal
Tags: Competitive Programming, Computer Science, Programming, Data Structures, Cpp

Hello readers, in this blog we are going to learn about queues and I am going to give you in-depth insight to queues and we will also see some important and powerful methods that comes in C++ Standard Template Library. So, lets get started…

Photo by Levi Jones on Unsplash

Queue data structure is a linear data structure which follows a FIFO i.e. First In First Out order, so you might be asking what does that mean? it’s means is element which comes first in the queue will be the first to leave it. As you can see in the above image, in a waiting queue the person which comes first will be the first to leave the queue and whenever you find it difficult to understand anything about queue, you should compare it with a real life queue because the insertion and deletion operations in queue data structure are just like these real life queues.

There are four types of queues :

Simple Queue.

Circular Queue.

Priority Queue.

Doubly Ended Queue.

Circular Doubly Queue.

In this blog, we will not see priority queue and discuss the rest in detail so, lets begin.

Queue Operations & Methods

Front

Front is like a pointer(but not literally a pointer), it stores the index of the first element present in the queue. We will see how front is handled in the enqueue and dequeue operations.

Rear

Rear is just like front but it stores the index of last element present in the queue. We will see manipulation of rear in detail in enqueue and dequeue operations.

isfull

isFull method is used to check whether the queue is full or not. It’s implementation is simple, we have to just check if the rear is equal to last index of queue container. It is a boolean type function.

MAX_SIZE = n; // max_size is the maximum size of the queue container

bool isFull(){

return rear == MAX_SIZE-1 ;



isEmpty

isEmpty method is used to check whether the queue is empty or not. It is a boolean type function. It returns true if front is equal to -1 or front is greater than rear.

bool isEmpty(){

return front == -1;

}

Enqueue

Enqueue is the insertion operation of queue, insertion is done at the end of queue as done is a real life queue. On insertion the rear variable is updated to increase its value by one. Lets see the algorithmic steps to understand the enqueue operation better.

1) Check for isFull condition as we can’t insert a new element in a queue which is already filled.

if(isFull())

print(“Overflow”);

return; 2) If queue is not full then we again need to check for two more conditons. 3) If front == -1 then we will increment both front and rear. In the beginning we will initialize both front and rear with -1, and if front is equal to -1 then it indicates that queue is empty and we are adding first element to queue and hence both front and rear have same index value i.e. 0. 4) Else we will just increment rear as we have to add new element next to current last element and to do so we are incrementing rear. 5)At last we will just put the data at index rear in queue.

queue[rear] = data; 6) Return

Dequeue

Dequeue is the deletion operation of queue, deletion is done from the front of queue as done is a real life queue. On deletion the front variable is updated to decrease its value by one. Lets see the algorithmic procedure

1) Check for isEmpty condition as we can’t delete an element from a queue which is empty.

if(isEmpty())

print(“Underflow”);

return; 2) If queue is not empty then we again need to check for two more conditons. 3) If front == rear, this case arise only if there is only one element is present in queue, we will reset both front and rear as after deleting this last element the queue will be empty we will do so by setting value of front and rear to -1. 4) Else we will just increment front. 5) Return

Coding Implementation in C++

Implementation by simple array

An Empty queue can also be full !!!

Yes, it is a pretty big problem with a simple queue that even if there are few empty blocks present in queue it can still overflow. Lets see the scenerio in which this condition occur.

Suppose we have made a queue of 8 blocks and we have added 8 elements now, in this case queue is full and the rear is pointing to index 7, if we remove few elements suppose 4 elements from queue then the front variable which was pointing to 0 would now points to fifth element i.e. index 4. Now, we have 4 blocks empty but if we try to add a new element then we can’t do so because rear is already at the maximum possible index. So you can see even if we have 4 empty blocks we have encountered overflow condition that’s a huge wastage of storage. Hence, we need more efficient techniques to overcome this issue.

How To Overcome?

To overcome the issue discussed above we use different techniques and approaches. We can shift all the elements on deletion but shifting all elements is a little costly hence we come up with newer implementation ideas of queues and these queues solved the above mentioned issue really well.

We use circular queue in which we just handle the front and rear a little bit differently and it shows astonishing results. There are two more queues doubly queue and circular doubly queue these can also handle the issue really well and in all these three circular doubly is the best.

Circular Queue

As we have seen that, weight the condition that queue is full even if there are few vacant spaces in the queue. To overcome this limitations we use circular Queue which is just a little different approach and the array remains same we just code it in such a way that the queue behaves like it is circular with last element connected with the first. It follows just few changes and you will get much efficient circular queue which will ensure full storage utilization.

Implementation of circular array

We will see coding implementation of circular queue in C++ and once I show you the code I will explain everything in detail.

So, you can there is nothing special just we have taken mod while updating values of front and rear this will ensure that all spaces get utilized also, rear is initializes with MAXSIZE -1. Try to dry run with an example you will understand it yourself it’s pretty simple just give it a try you will learn a lot.

Queue STL

Standard template library of C++ has great inbuilt functions and method and we will them.

empty → Test whether container is empty.

Test whether container is empty. size → Return size of container .

Return size of container . front → Access front element of queue container.

Access front element of queue container. back → Access last element of queue container.

Access last element of queue container. push → Insert element in queue container..

Insert element in queue container.. emplace → Construct and insert element in queue container. .

Construct and insert element in queue container. . pop → Remove next element from queue container..

Remove next element from queue container.. swap → Swap contents of queue container.

Doubly Ended Queue (Deque)

Doubly Ended Queue, commonly referred to as deque is a special type of queue in which on can insert and delete at both the ends of the queue hence the name doubly ended. It makes some operations easier and helps in better storage utilization as compared to simple queue.

Deque

Implementation of deque is pretty simple and similar to simple queue just you have write two additional functions for insertion at front and deletion at end, also there might need to change few conditions a little. We can also implement deque using circular queue which makes it much more efficient.

I am leaving the implementation of deque as an exercise for you hoping that you will try yourself and trust me it will be fun and will give you a deeper insight in concepts of queue.

Deque in STL

In standard template library, we generally use deque as it is efficient and has many amazing inbuilt functions you can see yourself deque is equipped with much more functions as compared to simple queue.

Iterators:

begin → Return iterator to beginning of queue .

Return iterator to beginning of queue . end → Return iterator to end of queue container.

Return iterator to end of queue container. rbegin → Return reverse iterator to reverse beginning of queue.

Return reverse iterator to reverse beginning of queue. rend → Return reverse iterator to reverse end of queue container.

Return reverse iterator to reverse end of queue container. cbegin → Return const_iterator to beginning of queue .

Return const_iterator to beginning of queue . cend → Return const_iterator to end of queue .

Return const_iterator to end of queue . crbegin → Return const_reverse_iterator to reverse beginning of queue.

Return const_reverse_iterator to reverse beginning of queue. crend →Return const_reverse_iterator to reverse end of queue container.

Capacity

size → Return size of queue container.

Return size of queue container. max_size → Return maximum size of queue container.

Return maximum size of queue container. resize → Change size of queue container.

Change size of queue container. empty → Test whether container is empty of queue container.

Test whether container is empty of queue container. shrink_to_fit → Shrink to fit of queue container.

Element access:

at → Access element of queue container..

Access element of queue container.. front → Access first element of queue container..

Access first element of queue container.. back →Access last element of queue container..

Modifiers:

assign → Assign container content .

Assign container content . push_back → Add element at the end of queue .

Add element at the end of queue . push_front → Insert element at beginning of queue .

Insert element at beginning of queue . pop_back → Delete last element of queue .

Delete last element of queue . pop_front → Delete first element of queue .

Delete first element of queue . insert → Insert elements from the queue.

Insert elements from the queue. erase → Erase elements from the queue.

Erase elements from the queue. swap → Swap content of the queue.

Swap content of the queue. clear → Clear content of the queue.

Clear content of the queue. emplace → Construct and insert element at the queue.

Construct and insert element at the queue. emplace_front → Construct and insert element at beginning of the queue.

Construct and insert element at beginning of the queue. emplace_back → Construct and insert element at the end of the queue.

Construct and insert element at the end of the queue. swap → Exchanges the contents of two deque containers .

Here comes the end….

So that’s all, we have reached the end of blog and we have covered the basics of simple, doubly, circular queue and we have seen STL functions and methods for simple queue and deque. I hope you understood what we have discussed and do try to code all the queues yourself it will strengthen the concepts even more.

If you feel there is anything missing in the blog please let me know by leaving a response.

Photo by Adi Goldstein on Unsplash

Okay then keep learning and Happy Programming : — )