Theory PDF
Theory PDF
Like Stack data structure, Queue is also a linear data structure which follows a
particular order in which the operations are performed. The order is First In First Out
(FIFO) which means that the element which is inserted first in the queue will be the
first one to be removed from the queue. A good example of queue is any queue of
consumers for a resource where the consumer that came first is served first.
The difference between stacks and queues is in removing. In a stack we remove the
most recently added item; in a queue, we remove the least recently added item.
Operations on Queue: Mainly the following four basic operations are performed on
queue:
• Enqueue: Adds an item to the queue. If the queue is full, then it is said to be
an Overflow condition.
• Dequeue: Removes an item from the queue. The items are popped in the
same order in which they are pushed. If the queue is empty, then it is said to
be an Underflow condition.
• Front: Get the front item from queue.
• Rear: Get the last item from queue.
Consider an Array of size N is taken to implement a queue. Initially, the size of the
queue will be zero(0). The total capacity of the queue will be the size of the array i.e.
N. Now initially, the index front will be equal to 0, and rear will be equal to N-1. Every
time on inserting an item, the index rear will increment by one, so increment it
as: rear = (rear + 1)%N and everytime on removing an item, the front index will shift
to right by 1 place so increment it as: front = (front + 1)%N.
Example:
Array = queue[N].
front = 0, rear = N-1.
N = 5.
Operation 1:
enque(5);
front = 0,
rear = (N-1 + 1)%N = 0.
Queue contains: [5].
Operation 2:
enque(10);
front = 0,
rear = (rear + 1)%N = (0 + 1)%N = 1.
Queue contains: [5, 10].
Operation 3:
enque(15);
front = 0,
rear = (rear + 1)%N = (1 + 1)%N = 2.
Queue contains: [5, 10, 15].
Operation 4:
deque();
print queue[front];
front = (front + 1)%N = (0 + 1)%N = 1.
Queue contains: [10, 15].
// Driver code
int main()
{
Queue* queue = createQueue(1000);
enqueue(queue, 10);
enqueue(queue, 20);
enqueue(queue, 30);
enqueue(queue, 40);
return 0;
}
Run
Java
}
// Queue is full when size becomes equal to
// the capacity
boolean isFull(Queue queue)
{ return (queue.size == queue.capacity);
}
return this.array[this.front];
}
return this.array[this.rear];
}
}
// Driver class
public class Test
{
public static void main(String[] args)
{
Queue queue = new Queue(1000);
queue.enqueue(10);
queue.enqueue(20);
queue.enqueue(30);
queue.enqueue(40);
System.out.println(queue.dequeue() +
" dequeued from queuen");
20 enqueued to queue
30 enqueued to queue
40 enqueued to queue
Front item is 20
Rear item is 40
Syntax:
where,
data_type is the type of element to be stored
in the queue.
queue_name is the name of the queue data structure.
int main()
{
queue <int> gquiz;
gquiz.push(10);
gquiz.push(20);
gquiz.push(30);
return 0;
}
Run
Output:
gquiz.size() : 3
gquiz.front() : 10
gquiz.back() : 30
gquiz.pop() : 20 30
Queue interface in Java
The Queue interface is available in java.util package and extends the Collection
interface. The queue collection is used to hold the elements about to be processed
and provides various operations like the insertion, removal etc. It is an ordered list of
objects with its use limited to insert elements at the end of the list and deleting
elements from the start of list i.e. it follows the FIFO or the First-In-First-Out principle.
Being an interface the queue needs a concrete class for the declaration and the
most commonly used classes are the PriorityQueue and LinkedList in Java. It is to
be noted that both the implementations are not thread safe. PriorityBlockingQueue is
one alternative implementation if thread safe implementation is needed.
Methods in Queue:
• add()- This method is used to add elements at the tail of the queue. More
specifically, at the last of linkedlist if it is used, or according to the priority in
case of priority queue implementation.
• peek()- This method is used to view the head of a queue without removing it.
It returns Null if the queue is empty.
• element()- This method is similar to peek(). It throws
NoSuchElementException when the queue is empty.
• remove()- This method removes and returns the head of the queue. It throws
NoSuchElementException when the queue is empty.
• poll()- This method removes and returns the head of the queue. It returns null
if the queue is empty.
• size()- This method returns the no. of elements in the queue.
System.out.println(q);
Elements of queue-[0, 1, 2, 3, 4]
removed element-0
[1, 2, 3, 4]
head of queue-1
Size of queue-4
Circular Linked Lists
A circular linked list is a linked list where all nodes are connected to form a
circle. There is no NULL at the end. A circular linked list can be a singly
circular linked list or doubly circular linked list.
Implementation:
To implement a circular singly linked list, we take an external pointer that points to
the last node of the list. If we have a pointer last pointing to the last node, then last ->
next will point to the first node.
The pointer last points to node Z and last -> next points to the node P.
Why have we taken a pointer that points to the last node instead of first node?
For insertion of node in the beginning we need traverse the whole list. Also, for
insertion and the end, the whole list has to be traversed. If instead of start pointer we
take a pointer to the last node then in both the cases there won’t be any need to
traverse the whole list. So insertion in the begging or at the end takes constant time
irrespective of the length of the list.
Below is a sample program to create and traverse in a Circular Linked List in both
Java and C++:
C++
#include<bits/stdc++.h>
using namespace std;
return last;
}
return last;
}
}
while(p != last->next);
}
// Driver Program
int main()
{
struct Node *last = NULL;
traverse(last);
return 0;
}
Run
Java
Output:
6 4 2 8 12 10
1. Any node can be a starting point. We can traverse the whole list by starting
from any point. We just need to stop when the first visited node is visited
again.
2. Useful for implementation of a queue. Unlike this implementation, we don’t
need to maintain two pointers for front and rear if we use a circular linked list.
We can maintain a pointer to the last inserted node and front can always be
obtained as next of last.
3. Circular lists are useful in applications to repeatedly go around the list. For
example, when multiple applications are running on a PC, it is common for the
operating system to put the running applications on a list and then to cycle
through them, giving each of them a slice of time to execute, and then making
them wait while the CPU is given to another application. It is convenient for
the operating system to use a circular list so that when it reaches the end of
the list it can cycle around to the front of the list.
4. Circular Doubly Linked Lists are used for implementation of advanced data
structures like Fibonacci Heap.
• Method 1 (By making push operation costly): This method makes sure that
newly entered element is always at the front of 'q1', so that pop operation just
dequeues from 'q1'. The queue, 'q2' is used to put every new element at front
of 'q1'.
pop(s)
1) Dequeue an item from q1 and return it.
Implementation:
C++
// Stack class
class Stack
{
// Two inbuilt queues
queue<int> q1, q2;
public:
Stack()
{
curr_size = 0;
}
// Driver code
int main()
{
Stack s;
s.push(1);
s.push(2);
s.push(3);
Run
Java
Output :
current size: 3
3
2
1
current size: 1
• Method 2 (By making pop operation costly): In push operation, the new
element is always enqueued to q1. In pop() operation, if q2 is empty then all
the elements except the last, are moved to q2. Finally the last element is
dequeued from q1 and returned.
push(s, x)
1) Enqueue x to q1 (assuming size of q1 is unlimited).
pop(s)
1) One by one dequeue everything except the last element
from q1 and enqueue to q2.
2) Dequeue the last item of q1, the dequeued item
is the result, store it.
3) Swap the names of q1 and q2
4) Return the item stored in step 2.
// Swapping of names is done to avoid one more
// movement of all elements from q2 to q1.
Implementation:
#include<bits/stdc++.h>
using namespace std;
// Stack class
class Stack
{
queue<int> q1, q2;
int curr_size;
public:
Stack()
{
curr_size = 0;
}
void pop()
{
if (q1.empty())
return;
void push(int x)
{
q1.push(x);
curr_size++;
}
int top()
{
if (q1.empty())
return -1;
while( q1.size() != 1 )
{
q2.push(q1.front());
q1.pop();
}
int size()
{
return curr_size;
}
};
// Driver code
int main()
{
Stack s;
s.push(1);
s.push(2);
s.push(3);
s.push(4);
Run
Output :
current size: 4
4
3
2
current size: 2
Implementing Queue using Stack
Problem: Given a stack data structure with push and pop operations, the task is to
implement a queue using instances of stack data structure and operations on them.
Solution: A queue can be implemented using two stacks. Let the queue to be
implemented be q and stacks used to
implement q are stack1 and stack2 respectively.
• Method 1 (By making enQueue operation costly): This method makes sure
that oldest entered element(element inserted first) is always at the top of
stack1, so that deQueue operation just pops from stack1. To put the element
at top of stack1, stack2 is used. The idea is to while pushing an element, first
move all elements from stack1 to stack2, insert the new element to stack1
and then again move all elements from stack2 to stack1.
enQueue(q, x)
1) While stack1 is not empty, push everything from stack1 to stack2
.
2) Push x to stack1 (assuming size of stacks is unlimited).
3) Push everything back to stack1.
Here the time complexity will be O(n)
deQueue(q)
1) If stack1 is empty then print an error
2) Pop an item from stack1 and return it
Here time complexity will be O(1)
Implementation:
C++
struct Queue {
stack<int> s1, s2;
void enQueue(int x)
{
// Move all elements from s1 to s2
while (!s1.empty()) {
s2.push(s1.top());
s1.pop();
}
// Return top of s1
int x = s1.top();
s1.pop();
return x;
}
};
// Driver code
int main()
{
Queue q;
q.enQueue(1);
q.enQueue(2);
q.enQueue(3);
return 0;
}
Run
Java
Output:
enQueue(q, x)
1) Push x to stack1 (assuming size of stacks is unlimited).
Here time complexity will be O(1)
deQueue(q)
1) If both stacks are empty then error.
2) If stack2 is empty
While stack1 is not empty, push everything from stack1 to stac
k2.
3) Pop the element from stack2 and return it.
Here time complexity will be O(n)
Method 2 is better in performance than method 1. As Method 1 moves all the
elements twice in enQueue operation, while method 2 (in deQueue operation)
moves the elements once and moves elements only if stack2 is empty.
Implementation :
C++
struct Queue {
stack<int> s1, s2;
// if s2 is empty, move
// elements from s1
if (s2.empty()) {
while (!s1.empty()) {
s2.push(s1.top());
s1.pop();
}
}
// Driver code
int main()
{
Queue q;
q.enQueue(1);
q.enQueue(2);
q.enQueue(3);
return 0;
}
Run
Java
Output:
1 2 3
Sample Problems on Queue
Input : Q = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100]
k = 5
Output : Q = [50, 40, 30, 20, 10, 60, 70, 80, 90, 100]
Solution - The idea is to use an auxiliary stack and follow these steps to solve the
problem -
1. Create an empty stack.
2. One by one dequeue items from a given queue and push the dequeued items
to stack.
3. Enqueue the contents of stack at the back of the queue.
4. Reverse the whole queue.
Pseudo Code
/* Function to reverse the first K elements of the Queue */
void reverseQueueFirstKElements(k, Queue)
{
if (Queue.empty() == true || k > Queue.size())
return
if (k <= 0)
return
stack Stack
/* Push the first K elements into a Stack*/
for ( i = 1 to k) {
Stack.push(Queue.front())
Queue.pop()
}
/* Enqueue the contents of stack
at the back of the queue*/
while (!Stack.empty()) {
Queue.push(Stack.top())
Stack.pop()
}
/* Remove the remaining elements and
enqueue them at the end of the Queue*/
for (int i = 0 to i < Queue.size() - k) {
Queue.push(Queue.front())
Queue.pop()
}
}
Input :
arr[] = {1, 2, 3, 1, 4, 5, 2, 3, 6}
k = 3
Output :
3 3 4 5 5 5 6
void printKMax(arr[], n, k)
{
// Create a Double Ended Queue, Qi that will store indexes of array ele
ments
// The queue will store indexes of useful elements in every window and
it will
// maintain decreasing order of values from front to rear in Qi, i.e.,
// arr[Qi.front[]] to arr[Qi.rear()] are sorted in decreasing order
deque < int > Qi(k)