AfterAcademy Tech
•
27 Dec 2019

First, let us see the properties of data structures that we already do know and build-up our concepts towards the queue data structure.
→ Following a similar definition, a queue is a container where only the front and back elements can be accessed or operated upon.
Queue is a data structure following the FIFO(First In, First Out) principle.

You can imagine an actual queue for a ticket counter for visual aid.

Enqueue Operation
Enqueue means inserting an element in the queue. In a normal queue at a ticket counter, where does a new person go and stand to become a part of the queue? The person goes and stands in the back. Similarly, a new element in a queue is inserted at the back of the queue.
Dequeue Operation
Dequeue means removing an element from the queue. Since queue follows the FIFO principle we need to remove the element of the queue which was inserted at first. Naturally, the element inserted first will be at the front of the queue so we will remove the front element and let the element behind it be the new front element.
Front Operation
This is similar to the peek operation in stacks, it returns the value of the element at the front without removing it.
isEmpty: Check if the queue is empty
To prevent performing operations on an empty queue, the programmer is required to internally maintain the size of the queue which will be updated during enqueue and deque operations accordingly. isEmpty() conventionally returns a boolean value: True if size is 0, else False.
The key idea of queue implementation is to use both ends of the queue: front end for deleting elements and back/rear end for inserting elements. This is as simple as its concept because we just need to simply keep in mind all of its properties and operations.
You should remember one very important thing though →
All operations in the queue must be of O(1) time complexity.
We shall be implementing queue in two different ways by changing the underlying container: Array and Linked List.
An array is one of the simplest containers offering random access to users based on indexes. But what are the access criteria in a queue? Can you access any element of the queue? No. Only the first and last elements are accessible in a queue.
So first, after initializing an array, we need two pointers, one each to point to the front end and back end. Instead of using actual pointers, we will use indexes, front and back will hold index positions of front end and back end respectively.
int queue[8]
int front = -1
int back = -1
Here, 8 is a pre-defined capacity of the queue. The size of a queue should not exceed this limit
★ The default value for front and back is -1, denoting that the queue is empty. Let us wrap this group of data members in a class:
class Queue
{
int arr[]
int capacity
int front
int back
}
Let us also create a constructor which initializes capacity, front and back.
Queue(int cap)
{
capacity = cap
front = -1
back = -1
}
★ You are also required to allocate memory to arr according to the conventions of the language you use to implement it.
In order to improve our memory utilization, we will implement what's known as a circular queue. To implement a circular queue, we would need a circular array. Don't stress, its quite similar to our normal array. So much that its declaration is not even slightly different.
In a circular array, the index after the last element is that of the first element.

So how would we traverse in such an array? Should we keep an if statement to check if the index is less than 8 or not? Alternatively, we could always use the remainder after dividing by 8 when increasing index.
arr[(i+1) % capacity]
That is primarily the only difference. Whenever we increase index in a circular array, we take modulo with the size of the array to proceed to the next element.
Now, we need to implement the operations that we generally perform in a queue.
Enqueue Operation
Where do we insert an element in a queue? In the back.
And how do we do it?

Simple, right? Any exceptions that come to mind?
▹ What if the queue is filled up to its capacity? We shall check if the queue if full before inserting a new element and throw an error if it is. How do we check if the queue is full? (Think!)
Now, let us implement this simply
void enqueue(int item)
{
if ((back + 1) % capacity == front)
print("Queue if full!")
else
{
back = (back+1) % capacity
arr[back] = item
if(front == -1)
front = back
}
}
Dequeue Operation
Now that we have discussed the insertion of an element, let’s discuss the deletion of an element. Which end of the queue do we use to delete an element? The front end! How do we delete an element?

▹ Can you think of an exception in this case like the one above of the queue is full? Ans: The queue can be empty when the dequeue operation is called. We need to check for this beforehand.
Let’s try implementing it now
int dequeue()
{
if(isEmpty() == True)
{
print("Queue is empty!")
return 0
}
else
{
int item = arr[front]
if(front == back)
front = back = -1
else
front = (front + 1) % capacity
return item
}
}
★ But we just implemented Step 1, right? Where's Step 2?
→ Well, Step 2 is mainly deallocating any memory assigned to the element being dequeued. We were dealing with only primitive data type integer and therefore didn't need to deallocate anything.
Peek and isEmpty Operation
peek() and isEmpty() is quite simple to implement. We need to steer clear of exceptions though.
int peek()
{
if (isEmpty() == True)
{
print("Queue is empty!")
return -1
}
else
return arr[front]
}
bool isEmpty()
{
if (front == -1)
return True
else
return False
}
Another way to implement a queue is to use a linked list as an underlying container. Let’s move towards the implementation part then
→ Let us assume that we have used class for linked list
class ListNode
{
int val
ListNode next
}
We also need two pointers that point to its front node and tail node.
What should an empty queue look like if implemented with a linked list? Ans: Its front and the back pointer will point to NULL
ListNode front = NULL
ListNode back = NULL
Is there any benefit to implementing the queue with a linked list compared to arrays?Ans: We do not need to mention the size of the queue beforehand.
→ Although, if you want to implement a limit to prevent excess use, you may need to encapsulate the class ListNode inside some other class along with a data member capacity.
class Queue
{
int capacity
class ListNode
{
int val
ListNode next
}
ListNode front
ListNode back
}
We shall be using just using class ListNode below for simplicity.Let us move towards queue operations
Enqueue Operation
The same properties hold as mentioned above with an added benefit that we need not worry about the queue being full, but we do need to check if its the first element is inserted in the queue, because the values of front and back in that case would be NULL.

void enqueue(int item)
{
ListNode temp = ListNode(item)
if( isEmpty() == True )
{
front = temp
back = temp
}
else
{
back.next = temp
back = back.next
}
}
Dequeue Operation
Properties of linked list relaxed us from checking if the queue is full in enqueue operation. Do they provide any relaxation for exceptions in dequeue operation? No. We still need to check if the queue is empty.
Since the front element is represented by a front pointer in this implementation. How do we delete the first element in a linked list? Simple, we make a front point to the second element

int dequeue()
{
if ( isEmpty() == True )
{
print ("Queue is empty!")
return 0
}
else
{
ListNode temp = front
int item = front.val
front = front.next
free(temp)
return item
}
}
Peek and isEmpty Operation
The implementation of these two operations is pretty simple and straight-forward in linked list too.
int peek()
{
if (isEmpty() == True)
{
print ("Queue is empty!")
return -1
}
else
return front.val
}
bool isEmpty()
{
if (front == NULL)
return True
else
return False
}
You can augment the queue data structure according to your needs. You can implement some extra operations like:-
What is the normal real-life application of queues? People wait in queues to await their chance to receive a service. Programming follows a similar concept.Let us look at some application of queue data structure in real-life applications :-
Happy Coding! Enjoy Algorithms!
AfterAcademy Tech
Stack is a very useful concept that a good programmer could use for their benefit. We will be discussing the basic design of stack and the operations that can be performed on it. We shall be also discussing the implementation of stack.

AfterAcademy Tech
In this blog, we will learn what an Operating System is and what are the goals of an Operating System. We will also learn the functionalities of an Operating System that helps in achieving the goal of the OS.

AfterAcademy Tech
Priority Queues are similar to queues where we insert an element from the back and remove an element from the front, but with one difference that the logical order of elements in the priority queue depends on the priority of the elements. In this blog, we will discuss the priority queues, its properties, its various implementations, and its applications. We will also analyze and compare the various implementations of the Priority Queue.

AfterAcademy Tech
Heap is a very useful data structure that every programmer should know well. The heap data structure is used in Heap Sort, Priority Queues. The understanding of heaps helps us to know about memory management. In this blog, we will discuss various operations that are done on heaps.
