0% found this document useful (0 votes)
17 views40 pages

DSA UNIT 2 (1)

The document provides a comprehensive overview of the Stack Abstract Data Type (ADT), explaining its LIFO (Last In First Out) principle, types (fixed and dynamic size), and basic operations such as push, pop, and top. It also discusses the advantages and disadvantages of using stacks, their applications, and the process of converting infix expressions to postfix notation using stacks. Additionally, it briefly introduces the Queue ADT, highlighting its FIFO (First In First Out) principle and basic operations.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
17 views40 pages

DSA UNIT 2 (1)

The document provides a comprehensive overview of the Stack Abstract Data Type (ADT), explaining its LIFO (Last In First Out) principle, types (fixed and dynamic size), and basic operations such as push, pop, and top. It also discusses the advantages and disadvantages of using stacks, their applications, and the process of converting infix expressions to postfix notation using stacks. Additionally, it briefly introduces the Queue ADT, highlighting its FIFO (First In First Out) principle and basic operations.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 40

UNIT-2

STACK ADT

What is Stack Data Structure?


Stack Data Structure is a linear data structure that follows LIFO (Last
In First Out) Principle , so the last element inserted is the first to be
popped out. In this article, we will cover all the basics of Stack,
Operations on Stack, its implementation, advantages, disadvantages
which will help you solve all the problems based on Stack.

What is Stack Data Structure?


Stack is a linear data structure based on LIFO(Last In First Out)
principle in which the insertion of a new element and removal of an
existing element takes place at the same end represented as the top of
the stack.
To implement the stack, it is required to maintain the pointer to the top
of the stack , which is the last element to be inserted because we can
access the elements only on the top of the stack.
LIFO(Last In First Out) Principle in Stack Data Structure:
This strategy states that the element that is inserted last will come out
first. You can take a pile of plates kept on top of each other as a real-life
example. The plate which we put last is on the top and since we remove
the plate that is at the top, we can say that the plate that was put last
comes out first.

Representation of Stack Data Structure:


Stack follows LIFO (Last In First Out) Principle so the element which is
pushed last is popped first.

Types of Stack Data Structure:


• Fixed Size Stack: As the name suggests, a fixed size stack
has a fixed size and cannot grow or shrink dynamically. If the
stack is full and an attempt is made to add an element to it, an
overflow error occurs. If the stack is empty and an attempt is
made to remove an element from it, an underflow error occurs.
• Dynamic Size Stack: A dynamic size stack can grow or
shrink dynamically. When the stack is full, it automatically
increases its size to accommodate the new element, and when
the stack is empty, it decreases its size. This type of stack is
implemented using a linked list, as it allows for easy resizing of
the stack.

Basic Operations on Stack Data Structure:


In order to make manipulations in a stack, there are certain operations
provided to us.
• push() to insert an element into the stack
• pop() to remove an element from the stack
• top() Returns the top element of the stack.
• isEmpty() returns true if stack is empty else false.
• isFull() returns true if the stack is full else false.
Push Operation in Stack Data Structure:
Adds an item to the stack. If the stack is full, then it is said to be
an Overflow condition.
Algorithm for Push Operation:
• Before pushing the element to the stack, we check if the stack
is full .
• If the stack is full (top == capacity-1) , then Stack
Overflows and we cannot insert the element to the stack.
• Otherwise, we increment the value of top by 1 (top = top +
1) and the new value is inserted at top position .
• The elements can be pushed into the stack till we reach
the capacity of the stack.
Pop Operation in Stack Data Structure:
Removes an item from the stack. The items are popped in the reversed
order in which they are pushed. If the stack is empty, then it is said to be
an Underflow condition.
Algorithm for Pop Operation:
• Before popping the element from the stack, we check if the
stack is empty .
• If the stack is empty (top == -1), then Stack Underflows and
we cannot remove any element from the stack.
• Otherwise, we store the value at top, decrement the value of
top by 1 (top = top – 1) and return the stored top value.
Top or Peek Operation in Stack Data Structure:
Returns the top element of the stack.
Algorithm for Top Operation:
• Before returning the top element from the stack, we check if
the stack is empty.
• If the stack is empty (top == -1), we simply print “Stack is
empty”.
• Otherwise, we return the element stored at index = top .
isEmpty Operation in Stack Data Structure:
Returns true if the stack is empty, else false.
Algorithm for isEmpty Operation:
• Check for the value of top in stack.
• If (top == -1) , then the stack is empty so return true .
• Otherwise, the stack is not empty so return false .

isFull Operation in Stack Data Structure:


Returns true if the stack is full, else false.
Algorithm for isFull Operation:
• Check for the value of top in stack.
• If (top == capacity-1), then the stack is full so return true.
• Otherwise, the stack is not full so return false.
EXAMPLE : REF LAB EXERCISE STACK PROGRAM

Complexity Analysis of Operations on Stack Data Structure:

Operations Time Complexity


Space Complexity

push() O(1) O(1)

pop() O(1) O(1)

top() or peek() O(1) O(1)

isEmpty() O(1) O(1)

isFull() O(1) O(1)

Advantages of Stack Data Structure:


• Simplicity: Stacks are a simple and easy-to-understand data
structure, making them suitable for a wide range of
applications.
• Efficiency: Push and pop operations on a stack can be
performed in constant time (O(1)) , providing efficient access to
data.
• Last-in, First-out (LIFO): Stacks follow the LIFO principle,
ensuring that the last element added to the stack is the first
one removed. This behavior is useful in many scenarios, such
as function calls and expression evaluation.
• Limited memory usage: Stacks only need to store the
elements that have been pushed onto them, making them
memory-efficient compared to other data structures.

Disadvantages of Stack Data Structure:


• Limited access: Elements in a stack can only be accessed
from the top, making it difficult to retrieve or modify elements in
the middle of the stack.
• Potential for overflow: If more elements are pushed onto
a stack than it can hold, an overflow error will occur, resulting
in a loss of data.
• Not suitable for random access: Stacks do not allow for
random access to elements, making them unsuitable for
applications where elements need to be accessed in a specific
order.
• Limited capacity: Stacks have a fixed capacity, which can
be a limitation if the number of elements that need to be stored
is unknown or highly variable.
Applications of Stack Data Structure:
• Infix to Postfix /Prefix conversion
• Redo-undo features at many places like editors, photoshop.
• Forward and backward features in web browsers
• In Memory management, any modern computer uses a stack
as the primary management for a running purpose. Each
program that is running in a computer system has its own
memory allocations.
• Stack also helps in implementing function call in computers.
The last called function is always completed first.

ARITHMETIC EXPRESSION EVALUATION


The stack organization is very effective in evaluating arithmetic
expressions. Expressions are usually represented in what is known
as Infix notation, in which each operator is written between two
operands (i.e., A + B). With this notation, we must distinguish between (
A + B )*C and A + ( B * C ) by using either parentheses or some
operator-precedence convention. Thus, the order of operators and
operands in an arithmetic expression does not uniquely determine the
order in which the operations are to be performed.

1. Polish notation (prefix notation) –


It refers to the notation in which the operator is placed before its two
operands. Here no parentheses are required, i.e.,
+AB

2. Reverse Polish notation(postfix notation) –


It refers to the analogous notation in which the operator is placed after
its two operands. Again, no parentheses is required in Reverse Polish
notation, i.e.,
AB+

Stack-organized computers are better suited for post-fix notation than


the traditional infix notation. Thus, the infix notation must be converted
to the postfix notation. The conversion from infix notation to postfix
notation must take into consideration the operational hierarchy.

There are 3 levels of precedence for 5 binary operators as given


below:

Highest: Exponentiation (^)

Next highest: Multiplication (*) and division (/)

Lowest: Addition (+) and Subtraction (-)

For example –
Infix notation: (A-B)*[C/(D+E)+F]

Post-fix notation: AB- CDE +/F +*

Here, we first perform the arithmetic inside the parentheses (A-B) and
(D+E). The division of C/(D+E) must be done prior to the addition with
F. After that multiply the two terms inside the parentheses and bracket.

Now we need to calculate the value of these arithmetic operations by


using a stack.

The procedure for getting the result is:

1. Convert the expression in Reverse Polish notation(post-fix


notation).
2. Push the operands into the stack in the order they appear.
3. When any operator encounters then pop two topmost
operands for executing the operation.
4. After execution push the result obtained into the stack.
5. After the complete execution of expression, the final result
remains on the top of the stack.
For example –
Infix notation: (2+4) * (4+6)
Post-fix notation: 2 4 + 4 6 + *

Result: 60

The stack operations for this expression evaluation is shown below:

Convert Infix expression to Postfix expression


Infix expression: The expression of the form “a operator b” (a + b)
i.e., when an operator is in-between every pair of operands.
Postfix expression: The expression of the form “a b operator” (ab+)
i.e., When every pair of operands is followed by an operator.
Examples:
Input: A + B * C + D
Output: ABC*+D+
Input: ((A + B) – C * (D / E)) + F
Output: AB+CDE/*-F+
Why postfix representation of the expression?
The compiler scans the expression either from left to right or from right
to left.
Consider the expression: a + b * c + d
• The compiler first scans the expression to evaluate the
expression b * c, then again scans the expression to add a to
it.
• The result is then added to d after another scan.
The repeated scanning makes it very inefficient. Infix expressions are
easily readable and solvable by humans whereas the computer cannot
differentiate the operators and parenthesis easily so, it is better to
convert the expression to postfix(or prefix) form before evaluation.
The corresponding expression in postfix form is abc*+d+. The postfix
expressions can be evaluated easily using a stack.
How to convert an Infix expression to a Postfix
expression?
To convert infix expression to postfix expression, use the stack data
structure. Scan the infix expression from left to right. Whenever we get
an operand, add it to the postfix expression and if we get an operator or
parenthesis add it to the stack by maintaining their precedence.
Below are the steps to implement the above idea:
1. Scan the infix expression from left to right.
2. If the scanned character is an operand, put it in the postfix
expression.
3. Otherwise, do the following
• If the precedence and associativity of the scanned
operator are greater than the precedence and
associativity of the operator in the stack [or the stack
is empty or the stack contains a ‘(‘ ], then push it in the
stack. [‘^‘ operator is right associative and other
operators like ‘+‘,’–‘,’*‘ and ‘/‘ are left-associative].
o Check especially for a condition when
the operator at the top of the stack and
the scanned operator both are ‘^‘. In
this condition, the precedence of the
scanned operator is higher due to its
right associativity. So it will be pushed
into the operator stack.
o In all the other cases when the top of
the operator stack is the same as the
scanned operator, then pop the
operator from the stack because of left
associativity due to which the scanned
operator has less precedence.
• Else, Pop all the operators from the stack which are
greater than or equal to in precedence than that of the
scanned operator.
o After doing that Push the scanned
operator to the stack. (If you encounter
parenthesis while popping then stop
there and push the scanned operator in
the stack.)
4. If the scanned character is a ‘(‘, push it to the stack.
5. If the scanned character is a ‘)’, pop the stack and output it
until a ‘(‘ is encountered, and discard both the parenthesis.
6. Repeat steps 2-5 until the infix expression is scanned.
7. Once the scanning is over, Pop the stack and add the
operators in the postfix expression until it is not empty.
8. Finally, print the postfix expression.
Illustration:
Follow the below illustration for a better understanding
Consider the infix expression exp = “a+b*c+d”
and the infix expression is scanned using the iterator i, which is
initialized as i = 0.
1st Step: Here i = 0 and exp[i] = ‘a’ i.e., an operand. So add this in the
postfix expression. Therefore, postfix = “a”.

2nd Step: Here i = 1 and exp[i] = ‘+’ i.e., an operator. Push this into the
stack. postfix = “a” and stack = {+}.
3rd Step: Now i = 2 and exp[i] = ‘b’ i.e., an operand. So add this in the
postfix expression. postfix = “ab” and stack = {+}.

4th Step: Now i = 3 and exp[i] = ‘*’ i.e., an operator. Push this into the
stack. postfix = “ab” and stack = {+, *}.

5th Step: Now i = 4 and exp[i] = ‘c’ i.e., an operand. Add this in the
postfix expression. postfix = “abc” and stack = {+, *}.
6th Step: Now i = 5 and exp[i] = ‘+’ i.e., an operator. The topmost
element of the stack has higher precedence. So pop until the stack
becomes empty or the top element has less precedence. ‘*’ is popped
and added in postfix. So postfix = “abc*” and stack = {+}.

Now top element is ‘+‘ that also doesn’t have less precedence. Pop
it. postfix = “abc*+”.
Now stack is empty. So push ‘+’ in the stack. stack = {+}.

7th Step: Now i = 6 and exp[i] = ‘d’ i.e., an operand. Add this in the
postfix expression. postfix = “abc*+d”.
Final Step: Now no element is left. So empty the stack and add it in the
postfix expression. postfix = “abc*+d+”.

EXAMPLE: REF INFIX TO POSTFIX PROGRAM


(WRITE THE STEPS LIKE THIS FOR YOUR LAB PROGRAM TOO)

QUEUE ADT
A Queue Data Structure is a fundamental concept in computer
science used for storing and managing data in a specific order. It
follows the principle of “First in, First out” (FIFO), where the first
element added to the queue is the first one to be removed. Queues are
commonly used in various algorithms and applications for their
simplicity and efficiency in managing data flow.

What is Queue in Data Structures?


A queue is a linear data structure that follows the First-In-First-Out
(FIFO) principle. It operates like a line where elements are added at
one end (rear) and removed from the other end (front).

Representation of Queues

Similar to the stack ADT, a queue ADT can also be implemented using
arrays, linked lists, or pointers. As a small example in this tutorial, we
implement queues using a one-dimensional array.
Basic Operations in Queue

Queue operations also include initialization of a queue, usage and


permanently deleting the data from the memory.

The most fundamental operations in the queue ADT include: enqueue(),


dequeue(), peek(), isFull(), isEmpty(). These are all built-in operations to
carry out data manipulation and to check the status of the queue.

Queue uses two pointers − front and rear. The front pointer accesses
the data from the front end (helping in enqueueing) while the rear pointer
accesses data from the rear end (helping in dequeuing).

Basic Operations of Queue Data Structure


• Enqueue (Insert): Adds an element to the rear of the queue.
• Dequeue (Delete): Removes and returns the element from the
front of the queue.
• Peek: Returns the element at the front of the queue without
removing it.
• Empty: Checks if the queue is empty.
• Full: Checks if the queue is full.
Queue Insertion Operation: Enqueue()

The enqueue() is a data manipulation operation that is used to insert


elements into the stack. The following algorithm describes the enqueue()
operation in a simpler way.

Algorithm
1. START
2. Check if the queue is full.
3. If the queue is full, produce overflow error and exit.
4. If the queue is not full, increment rear pointer to point
the next empty space.
5. Add data element to the queue location, where the rear
is pointing.
6. return success.
7. END

Queue Deletion Operation: dequeue()

The dequeue() is a data manipulation operation that is used to remove


elements from the stack. The following algorithm describes the
dequeue() operation in a simpler way.

Algorithm
1. START
2. Check if the queue is empty.
3. If the queue is empty, produce underflow error and exit.
4. If the queue is not empty, access the data where front
is pointing.
5. Increment front pointer to point to the next available
data element.
6. Return success.
7. END

Queue - The peek() Operation

The peek() is an operation which is used to retrieve the frontmost


element in the queue, without deleting it. This operation is used to check
the status of the queue with the help of the pointer.

Algorithm
1. START
2. Return the element at the front of the queue
3. END

Queue - The isFull() Operation

The isFull() operation verifies whether the stack is full.

Algorithm
1. START
2. If the count of queue elements equals the queue size,
return true
3. Otherwise, return false
4. END
Queue - The isEmpty() operation

The isEmpty() operation verifies whether the stack is empty. This


operation is used to check the status of the stack with the help of top
pointer.

Algorithm
1. START
2. If the count of queue elements equals zero, return true
3. Otherwise, return false
4. END

Implementation of Queue:
• Sequential allocation: A queue can be implemented using
an array. It can organize a limited number of elements.
• Linked list allocation: A queue can be implemented
using a linked list. It can organize an unlimited number of
elements.
Advantages of Queue:
• A large amount of data can be managed efficiently with ease.
• Operations such as insertion and deletion can be performed
with ease as it follows the first in first out rule.
• Queues are useful when a particular service is used by
multiple consumers.
• Queues are fast in speed for data inter-process
communication.
• Queues can be used in the implementation of other data
structures.
Disadvantages of Queue:
• The operations such as insertion and deletion of elements from
the middle are time consuming.
• In a classical queue, a new element can only be inserted when
the existing elements are deleted from the queue.
• Searching an element takes O(N) time.
• Maximum size of a queue must be defined prior in case of
array implementation.

Applications of Queue
• Task scheduling in operating systems
• Data transfer in network communication
• Simulation of real-world systems (e.g., waiting lines)
• Priority queues for event processing queues for event
processing

CIRCULAR QUEUE
What is a Circular Queue?
A Circular Queue is an extended version of a normal queue where the
last element of the queue is connected to the first element of the queue
forming a circle.
The operations are performed based on FIFO (First In First Out)
principle. It is also called ‘Ring Buffer’.
In a normal Queue, we can insert elements until queue becomes full.
But once queue becomes full, we cannot insert the next element even if
there is a space in front of queue.

Operations on Circular Queue:


• Front: Get the front item from the queue.
• Rear: Get the last item from the queue.
• enQueue(value) This function is used to insert an element
into the circular queue. In a circular queue, the new element is
always inserted at the rear position.
o Check whether the queue is full – [i.e., the rear
end is in just before the front end in a circular
manner].
o If it is full then display Queue is full.
o If the queue is not full then, insert
an element at the end of the
queue.
• deQueue() This function is used to delete an element from
the circular queue. In a circular queue, the element is always
deleted from the front position.
o Check whether the queue is Empty.
o If it is empty then display Queue is empty.
o If the queue is not empty, then get
the last element and remove it
from the queue.
Illustration of Circular Queue Operations:
Follow the below image for a better understanding of the enqueue and
dequeue operations.

How to Implement a Circular Queue?


A circular queue can be implemented using two data structures:

• Array
• Linked List
Here we have shown the implementation of a circular queue using an
array data structure.
Implement Circular Queue using Array:
1. Initialize an array queue of size n, where n is the maximum
number of elements that the queue can hold.
2. Initialize two variables front and rear to -1.
3. Enqueue: To enqueue an element x into the queue, do the
following:
• Increment rear by 1.
o If rear is equal to n, set rear to 0.
• If front is -1, set front to 0.
• Set queue[rear] to x.
4. Dequeue: To dequeue an element from the queue, do the
following:
• Check if the queue is empty by checking if front is -1.
o If it is, return an error message
indicating that the queue is empty.
• Set x to queue[front].
• If front is equal to rear, set front and rear to -1.
• Otherwise, increment front by 1 and if front is equal
to n, set front to 0.
• Return x.

#include<bits/stdc++.h>
using namespace std;

class Queue
{
// Initialize front and rear
int rear, front;

// Circular Queue
int size;
int *arr;
public:
Queue(int s)
{
front = rear = -1;
size = s;
arr = new int[s];
}

void enQueue(int value);


int deQueue();
void displayQueue();
};

/* Function to create Circular queue */


void Queue::enQueue(int value)
{
if ((front == 0 && rear == size-1) ||
((rear+1) % size == front))
{
printf("\nQueue is Full");
return;
}

else if (front == -1) /* Insert First Element */


{
front = rear = 0;
arr[rear] = value;
}
else if (rear == size-1 && front != 0)
{
rear = 0;
arr[rear] = value;
}

else
{
rear++;
arr[rear] = value;
}
}

// Function to delete element from Circular Queue


int Queue::deQueue()
{
if (front == -1)
{
printf("\nQueue is Empty");
return INT_MIN;
}

int data = arr[front];


arr[front] = -1;
if (front == rear)
{
front = -1;
rear = -1;
}
else if (front == size-1)
front = 0;
else
front++;

return data;
}

// Function displaying the elements


// of Circular Queue
void Queue::displayQueue()
{
if (front == -1)
{
printf("\nQueue is Empty");
return;
}
printf("\nElements in Circular Queue are: ");
if (rear >= front)
{
for (int i = front; i <= rear; i++)
printf("%d ",arr[i]);
}
else
{
for (int i = front; i < size; i++)
printf("%d ", arr[i]);

for (int i = 0; i <= rear; i++)


printf("%d ", arr[i]);
}
}

/* Driver of the program */


int main()
{
Queue q(5);

// Inserting elements in Circular Queue


q.enQueue(14);
q.enQueue(22);
q.enQueue(13);
q.enQueue(-6);

// Display elements present in Circular Queue


q.displayQueue();

// Deleting elements from Circular Queue


printf("\nDeleted value = %d", q.deQueue());
printf("\nDeleted value = %d", q.deQueue());

q.displayQueue();

q.enQueue(9);
q.enQueue(20);
q.enQueue(5);

q.displayQueue();

q.enQueue(20);
return 0;
}

Output
Elements in Circular Queue are: 14 22 13 -6

Deleted value = 14

Deleted value = 22
Elements in Circular Queue are: 13 -6

Elements in Circular Queue are: 13 -6 9 20 5

Queue is Full

Complexity Analysis of Circular Queue Operations:


• Time Complexity:
o Enqueue: O(1) because no loop is involved for a
single enqueue.
o Dequeue: O(1) because no loop is involved for
one dequeue operation.
• Auxiliary Space: O(N) as the queue is of size N.
Applications of Circular Queue:
1. Memory Management: The unused memory locations in
the case of ordinary queues can be utilized in circular queues.
2. Traffic system: In computer controlled traffic system,
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.

PRIORITY QUEUE
What is Priority Queue
A priority queue is a type of queue that arranges elements based on
their priority values. Elements with higher priority values are typically
retrieved before elements with lower priority values.
In a priority queue, each element has a priority value associated with it.
When you add an element to the queue, it is inserted in a position
based on its priority value. For example, if you add an element with a
high priority value to a priority queue, it may be inserted near the front
of the queue, while an element with a low priority value may be inserted
near the back.

There are several ways to implement a priority queue, including using


an array, linked list, heap, or binary search tree. Each method has its
own advantages and disadvantages, and the best choice will depend
on the specific needs of your application.

Priority queues are often used in real-time systems, where the order in
which elements are processed can have significant consequences.
They are also used in algorithms to improve their efficiencies, such
as Dijkstra’s algorithm for finding the shortest path in a graph and the
A* search algorithm for pathfinding.
Properties of Priority Queue
So, a priority Queue is an extension of the queue with the
following properties.
• Every item has a priority associated with it.
• An element with high priority is dequeued before an element
with low priority.
• If two elements have the same priority, they are served
according to their order in the queue.
In the below priority queue, an element with a maximum ASCII value
will have the highest priority. The elements with higher priority are
served first.
How is Priority assigned to the elements in a Priority Queue?
In a priority queue, generally, the value of an element is considered for
assigning the priority.

For example, the element with the highest value is assigned the highest
priority and the element with the lowest value is assigned the lowest
priority. The reverse case can also be used i.e., the element with the
lowest value can be assigned the highest priority. Also, the priority can
be assigned according to our needs.

Operations of a Priority Queue:


A typical priority queue supports the following operations:

1) Insertion in a Priority Queue


When a new element is inserted in a priority queue, it moves to the
empty slot from top to bottom and left to right. However, if the element
is not in the correct place then it will be compared with the parent node.
If the element is not in the correct order, the elements are swapped.
The swapping process continues until all the elements are placed in the
correct position.
2) Deletion in a Priority Queue
As you know that in a max heap, the maximum element is the root
node. And it will remove the element which has maximum priority first.
Thus, you remove the root node from the queue. This removal creates
an empty slot, which will be further filled with new insertion. Then, it
compares the newly inserted element with all the elements inside the
queue to maintain the heap invariant.

3) Peek in a Priority Queue


This operation helps to return the maximum element from Max Heap or
the minimum element from Min Heap without deleting the node from the
priority queue.

Types of Priority Queue:


1) Ascending Order Priority Queue
As the name suggests, in ascending order priority queue, the element
with a lower priority value is given a higher priority in the priority list. For
example, if we have the following elements in a priority queue arranged
in ascending order like 4,6,8,9,10. Here, 4 is the smallest number,
therefore, it will get the highest priority in a priority queue and so when
we dequeue from this type of priority queue, 4 will remove from the
queue and dequeue returns 4.

2) Descending order Priority Queue


The root node is the maximum element in a max heap, as you may
know. It will also remove the element with the highest priority first. As a
result, the root node is removed from the queue. This deletion leaves
an empty space, which will be filled with fresh insertions in the future.
The heap invariant is then maintained by comparing the newly inserted
element to all other entries in the queue.
Difference between Priority Queue and Normal Queue?
There is no priority attached to elements in a queue, the rule of first-in-
first-out(FIFO) is implemented whereas, in a priority queue, the
elements have a priority. The elements with higher priority are served
first.

How to Implement Priority Queue?


Priority queue can be implemented using the following data structures:

• Arrays
• Linked list
• Heap data structure
• Binary search tree
EXAMPLE: REF PRIORITY QUEUE PROGRAM(LAB EXERCISE)

DEQUE:
Deque is a type of queue in which insert and deletion can be performed
from either front or rear. It does not follow the FIFO rule. It is also
known as double-ended queue
Operations on Deque:
Deque consists of mainly the following operations:

• Insert Front
• Insert Rear
• Delete Front
• Delete Rear

1. Insert at the Front: This operation is used to add an element


at the front. If the number of elements is not exceeding the size of the
deque then only insertion takes place. This operation takes constant
time. Time Complexity: O(1)
2. Insert at the Rear: If the deque is not full then this function
inserts the element at the back end of the queue. This operation done
in O(1) time.
3. Delete from the Front: This operation is used to delete an
element from the deque. If the deque is not empty then this operation
will delete an element from the front end of the deque. It’s time
Complexity is O(1).
4. Delete from the Rear: This operation is used to delete an
element from the back end of the deque if the deque is not empty. It’s
time Complexity is O(1).
Properties of Deque:
• Deque is a generalized version of the queue which allows us to
insert and delete the element at both ends.
• It does not follow FIFO (first in first out) rule.

Applications of Deque:
• It is used in job scheduling algorithms.
• It supports both stack and queue operations.
• The clockwise and anti-clockwise rotation operations in deque
are performed in O(1) time which is helpful in many problems.
Real-time Application of Deque:
• In a web browser’s history, recently visited URLs are added to
the front of the deque and the URL at the back of the deque is
removed after some specified number of operations of
insertions at the front.
• Storing a software application’s list of undo operations.
• In graph traversal algorithms such as breadth-first search
(BFS). BFS uses a deque to store nodes and performs
operations such as adding or removing nodes from both ends
of the deque.
• In task management systems to manage the order and priority
of incoming tasks. Tasks can be added to the front or back of
the deque depending on their priority or deadline.
• In queueing systems to manage the order of incoming
requests. Requests can be added to the front or back of the
deque depending on their priority or arrival time.
• In caching systems to cache frequently accessed data.
Deques can be used to store cached data and efficiently
support operations such as adding or removing data from both
ends of the deque.
Advantages of Deque:
• You are able to add and remove items from the both front and
back of the queue.
• Deques are faster in adding and removing the elements to the
end or beginning.
• The clockwise and anti-clockwise rotation operations are faster
in a deque.
• Dynamic Size: Deques can grow or shrink dynamically.
• Efficient Operations: Deques provide efficient O(1) time
complexity for inserting and removing elements from both
ends.
• Versatile: Deques can be used as stacks (LIFO) or queues
(FIFO), or as a combination of both.
• No Reallocation: Deques do not require reallocation of
memory when elements are inserted or removed.
• Thread Safe: Deques can be thread-safe if used with proper
synchronization.
• Cache-Friendly: Deques have a contiguous underlying
storage structure which makes them cache-friendly.
Disadvantages of Deque:
• Deque has no fixed limitations for the number of elements they
may contain. This interface supports capacity-restricted
deques as well as the deques with no fixed size limit.
• They are less memory efficient than a normal queue.
• Memory Overhead: Deques have higher memory overhead
compared to other data structures due to the extra pointers
used to maintain the double-ended structure.
• Synchronization: Deques can cause synchronization
issues if not used carefully in multi-threaded environments.
• Complex Implementation: Implementing a deque can be
complex and error-prone, especially if implementing it
manually.
• Not All Platforms: Deques may not be supported by all
platforms, and may need to be implemented manually.
• Not Suitable for Sorting: Deques are not designed for
sorting or searching, as these operations require linear time.
• Limited Functionality: Deques have limited functionality
compared to other data structures such as arrays, lists, or
trees.

You might also like