MODULE 02 STACK AND QUEUES
Stack
• A Stack is a linear data structure that follows the LIFO (Last-In-
First-Out) principle.
• It is a data structure where insertion and deletion takes place at
one end.
• It contains one pointer, top pointing to the topmost element of
the stack.
• Two operations performed on stack are push [to insert] and pop
[to delete] elements from the stack.
A stack can be defined as a container in which insertion and deletion
can be done from the one end known as the top of the stack.
Some key points related to stack
o It is called as stack because it behaves like a real-world stack,
piles of books, etc.
o A Stack is an abstract data type with a pre-defined capacity,
which means that it can store the elements of a limited size.
o Last item added to the stack is first to be removed.
o Stack can be implemented using array and linked list.
Array Implementation of stack
Stack is an ordered collection of elements and it can be implemented
using array.
Stack implemented using array is called static stack. Here array sizes
should be declared earlier.
Working of Stack [push and pop]
Stack works on the LIFO pattern. Suppose we want to store the
elements in a stack and let's assume that stack is empty. We have
taken the stack of size 5 as shown below in which we are pushing the
elements one by one until the stack becomes full.
The stack gets filled up from the bottom to the top.
When we perform the delete operation on the stack, there is only one
way for entry and exit as the other end is closed. It follows the LIFO
pattern, which means that the value entered first will be removed last.
Standard Stack Operations
The following are some common operations implemented on the
stack:
o push(): When we insert an element in a stack then the operation
is known as a push. If the stack is full then the overflow
condition occurs.
o pop(): When we delete an element from the stack, the operation
is known as a pop. If the stack is empty means that no element
exists in the stack, this state is known as an underflow state.
o isEmpty(): It determines whether the stack is empty or not.
o isFull(): It determines whether the stack is full or not.'
o peek(): Get the value of the top,without removing it.
o count(): It returns the total number of elements available in a
stack.
o change(): It changes the element at the given position.
o display(): It prints all the elements available in the stack.
PUSH operation
The steps involved in the PUSH operation is given below:
o Before inserting an element in a stack, we check whether the
stack is full.
o If we try to insert the element in a stack, and the stack is full,
then the overflow condition occurs.
o When we initialize a stack, we set the value of top as -1 to check
that the stack is empty.
o When the new element is pushed in a stack, first, the value of
the top gets incremented, i.e., top=top+1, and the element will
be placed at the new position of the top.
o The elements will be inserted until we reach the max size of the
stack.
Stack is a data structure implemented using array,
Max is the maximum size of the array,
Top is a pointer pointing to the top of the stack,
Item is the element to be inserted to the top of stack.
Algorithm: PUSH[Operation in Stack]
Step 1: If Top==Max-1
Print “Overflow : Stack is full” and Return Null
End If
Step 2: Top=Top+1
Step 3: Stack[Top]=Item
Step 4: Return
POP operation
The steps involved in the POP operation is given below:
o Before deleting the element from the stack, we check whether
the stack is empty.
o If we try to delete the element from the empty stack, then
the underflow condition occurs.
o If the stack is not empty, we first access the element which is
pointed by the top
o Once the pop operation is performed, the top is decremented by
1, i.e., top=top-1.
Stack is a data structure implemented using array. Top is a pointer
pointing to the top of the stack. Item is the element to be inserted to
the top of stack.
Algorithm: POP Operation in Stack
Step 1: If TOP==-1
Print “Underflow: Stack is empty” and Return Null
End if
Step 2: Set Item=Stack[Top]
Step 3: Top=Top-1
Step 4: Return Item
Applications of Stack Data Structure
Although stack is a simple data structure to implement, it is very
powerful. The most common uses of a stack are:
• To reverse a word - Put all the letters in a stack and pop them
out. Because of the LIFO order of stack, you will get the letters in
reverse order.
• In compilers - Compilers use the stack to calculate the value of
expressions like 2 + 4 / 5 * (7 - 9) by converting the expression to
prefix or postfix form. Expression evaluation and expression
conversion can be done using stack.
• In browsers - The back button in a browser saves all the URLs
you have visited previously in a stack. Each time you visit a new
page, it is added on top of the stack. When you press the back button,
the current URL is removed from the stack, and the previous URL is
accessed.
• Undo Mechanism
• Traversing tree and graph
• Backtracking - Suppose we have to create a path to solve a
maze problem. If we are moving in a particular path, and we realize
that we come on the wrong way. In order to come at the beginning of
the path to create a new path, we have to use the stack data structure.
• Memory management: The stack manages the memory. The
memory is assigned in the contiguous memory blocks.
• Recursion: The recursion means that the function is calling
itself again. To maintain the previous states, the compiler creates a
system stack in which all the previous records of the function are
maintained.
Application of stack –
Expression Conversion
Infix to postfix
Infix to prefix
Postfix to infix
Prefix to infix
The way to write arithmetic expression is known as a notation. An
arithmetic expression can be written in three different but equivalent
notations, i.e., without changing the essence or output of an
expression. These notations are −
• Infix Notation
• Prefix (Polish) Notation
• Postfix (Reverse-Polish) Notation
These notations are named as how they use operator in expression.
We shall learn the same here in this chapter.
Infix Notation
We write expression in infix notation, e.g. a - b + c, where operators
are used in-between operands. It is easy for us humans to read, write,
and speak in infix notation but the same does not go well with
computing devices. An algorithm to process infix notation could be
difficult and costly in terms of time and space consumption.
Prefix Notation
In this notation, operator is prefixed to operands, i.e. operator is
written ahead of operands. For example, +ab. This is equivalent to its
infix notation a + b. Prefix notation is also known as Polish
Notation.
Postfix Notation
This notation style is known as Reversed Polish Notation. In this
notation style, the operator is postfixed to the operands i.e., the
operator is written after the operands. For example, ab+. This is
equivalent to its infix notation a + b.
The following table briefly tries to show the difference in all three
notations −
[Link]. Infix Notation Prefix Notation Postfix Notation
1 a+b +ab ab+
2 (a + b) ∗ c ∗+abc ab+c∗
3 a ∗ (b + c) ∗a+bc abc+∗
4 a/b+c/d +/ab/cd ab/cd/+
5 (a + b) ∗ (c + d) ∗+ab+cd ab+cd+∗
Parsing Expressions
As we have discussed, it is not a very efficient way to design an
algorithm or program to parse infix notations. Instead, these infix
notations are first converted into either postfix or prefix
notations and then computed.
To parse any arithmetic expression, we need to take care of operator
precedence and associativity also.
Precedence
When there are more operators in an expressions the evaluation is
done using precedence. For example −
As multiplication operation has precedence over addition, b * c will
be evaluated first. A table of operator precedence is provided later.
Algorithm for infix to postfix conversion
1. Scan the infix expression from left to right.
2. If the scanned character is an operand, output it.
3. Else,
1 If the precedence of the scanned operator is greater than the
precedence of the operator in the stack(or the stack is empty or the
stack contains a ‘(‘ ), push it.
2 Else, Pop all the operators from the stack which are greater
than or equal to in precedence than that of the scanned operator.
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 an ‘(‘, push it to the stack.
5. If the scanned character is an ‘)’, pop the stack and output it until a
‘(‘ is encountered, and discard both the parenthesis.
6. Repeat steps 2-6 until infix expression is scanned.
7. Print the output
8. Pop and output from the stack until it is not empty.
A+B*(C^D-E)
(A+B/C*(D+E)-F)
Algorithm for infix to prefix conversion
As an example, consider the problem of finding the factorial of a positive
integer,
double fact(int n)
if (n = = 0)
return 1;
else
return n*fact(n-1);
}
STACK USING LINKED LIST
Advantages of Linked List
Dynamic Data Structure
Linked list is a dynamic data structure so it can grow and shrink at
runtime by allocating and de allocating memory. So there is no need
to give initial size of linked list.
Insertion and Deletion
Insertion and deletion of nodes are really easier. Unlike array here we
don’t have to shift elements after insertion or deletion of an element.
In linked list we just have to update the address present in next pointer
of a node.
Stack using linked list
Syntax of linked list
struct node
{
int data;
struct node *next
}*top;
push(value) - Inserting an element into the Stack
We can use the following steps to insert a new node into the stack
PUSH[Stack,Top,Item]
• Step 1 - Create a New node.
• Step 2 – Info[New Node]=Item
• Step 2 - Check whether stack is Empty (top == NULL)
• Step 3 – Top = Newnode
• Step 3 - If it is Empty, then set next[Top] = NULL.
• Step 4 - If it is Not Empty, then set next [NewNode]= top.
• Step 5 - Finally, set top = Newnode.
• Step 6 – Return
pop() - Deleting an Element from a Stack
We can use the following steps to delete a node from the stack.
PUSH[Stack,Top,]
• Step 1 - Check whether stack is Empty (top == NULL).
• Step 2 - If it is Empty, then display "Stack is Empty!!!
Deletion is not possible!!!" and terminate the function.
• Step 3 - If it is not empty set Top = Next[Top]
• Step 4 – Item = Info[Top]
• Step 5 - Return
Queue
A queue can be defined as an ordered list of elements which
enables insertion of elements can take place at only one end
called REAR and delete operations can take place at another end
called FRONT.
Queue is referred to be as First In First Out list. First element
entered the queue will be the first to go out.
For example, people waiting in line for a rail ticket form a queue.
Applications of Queue
Due to the fact that queue performs actions on first in first [Link]
are various applications of queues discussed as below.
1. Queues are widely used as waiting lists for a single shared
resource like printer, disk, CPU.
2. Queues are used in asynchronous transfer of data (where data is
not being transferred at the same rate between two processes)
for eg. pipes, file IO, sockets.
3. Queues are used as buffers in most of the applications like MP3
media player, CD player, etc.
4. Queues are used to maintain the play list in media players in
order to add and remove the songs from the play-list.
Basic Operations of Queue
A queue is an object (an abstract data structure - ADT) that allows
the following operations:
• Enqueue: Add an element to the end of the queue
• Dequeue: Remove an element from the front of the queue
• IsEmpty: Check if the queue is empty
• IsFull: Check if the queue is full
• Peek: Get the value of the front of the queue without removing it
Array Implementation of Queue
Queue is an ordered collection of elements and it can be
implemented using array.
A queue can be represented in a program by using a linear array of
elements and three pointer variables FRONT, REAR and MAX.
The queue is empty if FRONT = REAR = -1. The queue is full if FRONT
=1 and REAR = MAX - 1.
The size of the above queue is 5. It has 3 elements A, B and C. The FRONT,
REAR and MAX pointers will have values 0, 2 and 5 respectively.
Whenever an element is deleted from the queue the value of FRONT is
increased by 1;
FRONT:=FRONT +1
Whenever an element is inserted to the queue the value of REAR is
increased by 1;
REAR:=REAR +1
The REAR element of the queue will occupy QUEUE [N-1]; or the queue will
occupy the last part of the array. This occurs even though the queue itself
may not contain many elements. Ie,QUEUE IS FULL.
Enqueue Operation[insertion]
Queues maintain two data pointers, front and rear. Therefore, its
operations are comparatively difficult to implement than that of
stacks.
The following steps should be taken to enqueue (insert) data into a
queue −
• Step 1 − Check if the queue is full.
• Step 2 − If the queue is full, produce overflow error and exit.
• Step 3 − If the queue is not full, increment rear pointer to point
the next empty space.
• Step 4 − Add data element to the queue location, where the rear
is pointing.
• Step 5 − return success.
Dequeue Operation
Accessing data from the queue is a process of two tasks − access the
data where front is pointing and remove the data after access.
The following steps are taken to
perform dequeue operation[deletion operation] −
• Step 1 − Check if the queue is empty.
• Step 2 − If the queue is empty, produce underflow error and
exit.
• Step 3 − If the queue is not empty, access the data where front is
pointing.
• Step 4 − Increment front pointer to point to the next available
data element.
• Step 5 − Return success.
Algorithm: Insert Operation on Queue
INSERT[Queue,Front,Rear,Item]
Step 1: If Rear=Max-1
Print “Overflow : Queue is full” and Exit
Step2: Else IF Front = -1 and Rear = -1
SET FRONT = REAR = 0
Queue[Rear]=Item
Step 3: Else set Rear=Rear+1
Step 4: Queue[Rear]=Item
Step 5: End
Algorithm: Delete Operation on Queue
DELETE[Queue,Front,Rear,Item]
Step 1: If Front=-1 or Front >Rear
Print “Underflow: Queue is empty” and Exit
End if
Step 2: Set Item=Queue[Front]
Step 3: If Front=Rear
Set Front=Rear=-1
Else
Step 4; Front=Front+1
End If
Step 5: Return Item
Step 5: End
QUEUE USING LINKED LIST
Queue can also be represented using linked list. Lined list does not
have any restriction on elements it can hold.
struct node
{
int data;
struct node *next;
}*front, * rear;
The major problem with the queue implemented using an array is, It
will work for an only fixed number of data values. That means, the
amount of data must be specified at the beginning itself. Queue using
an array is not suitable when we don't know the size of data which we
are going to use.
The HEAD pointer of the linked list is used as the FRONT. Also, use another
pointer called REAR to store the address of the last element of the queue.
enQueue(value) - Inserting an element into the Queue
We can use the following steps to insert a new node into the queue...
• Step 1 - Create a newNode with info['newNode] =item and set
next['newNode] to NULL.
• Step 2 - Check whether queue is Empty (front==rear == NULL)
• Step 3 - If it is Empty then,
set front = newNode and rear = newNode.
• Step 4 - If it is Not Empty then,
set next[rear] = newNode and rear = newNode.
• Step 5 - Return
deQueue() - Deleting an Element from Queue
We can use the following steps to delete a node from the queue...
• Step 1 - Check whether queue is Empty (front ==
rear==NULL).
• Step 2 - If it is Empty, then display "Queue is Empty!!! Deletion
is not possible!!!" and terminate from the function
• Step 3 - If it is Not Empty then, define a Node pointer 'temp'
and set it to 'front'.
• Step 4 - Then set 'front = next[front] and delete 'temp'
(free(temp)).
DEQUE
dequeue stands for Double Ended Queue. In the queue, the insertion
takes place from one end while the deletion takes place from another
end. The end at which the insertion occurs is known as the rear end
whereas the end at which the deletion occurs is known as front end
Deque is a linear data structure in which the insertion and
deletion operations are performed from both ends. We can say that
deque is a generalized version of the queue.
There are two types of Queues, Input-restricted queue, and output-
restricted queue.
1. Input-restricted queue: The input-restricted queue means that
some restrictions are applied to the insertion. In input-restricted
queue, the insertion is applied to one end while the deletion is
applied from both the ends.
2. Output-restricted queue: The output-restricted queue means
that some restrictions are applied to the deletion operation. In an
output-restricted queue, the deletion can be applied only from
one end, whereas the insertion is possible from both ends.
Operations on Deque
The following are the operations applied on deque:
o Insert at front
o Delete from end
o insert at rear
o delete from rear
Other than insertion and deletion, we can also perform peek operation
in deque. Through peek operation, we can get the front and
the rear element of the dequeue.
We can perform two more operations on dequeue:
o isFull(): This function returns a true value if the stack is full;
otherwise, it returns a false value.
o isEmpty(): This function returns a true value if the stack is
empty; otherwise it returns a false value.
Memory Representation
The deque can be implemented using two data structures,
i.e., circular array, and doubly linked list.
Circular Queue
Why was the concept of the circular queue introduced?
There was one limitation in the array implementation of Queue. If the
rear reaches to the end position of the Queue then there might be
possibility that some vacant spaces are left in the beginning which
cannot be utilized. So, to overcome such limitations, the concept of the
circular queue was introduced.
WORKING EXPLAINED
[As we can see in the above image, the rear is at the last position of
the Queue and front is pointing somewhere rather than the
0th position. In the above array, there are only two elements and other
three positions are empty. The rear is at the last position of the Queue;
if we try to insert the element then it will show that there are no empty
spaces in the Queue. There is one solution to avoid such wastage of
memory space by shifting both the elements at the left and adjust the
front and rear end accordingly. It is not a practically good approach
because shifting all the elements will consume lots of time. The
efficient approach to avoid the wastage of the memory is to use the
circular queue data structure.]
When rear = -1 we set rear = 0 and when front = n-1 we set front =0
What is a Circular Queue?
A circular queue is similar to a linear queue as it is also based on
the FIFO (First In First Out) principle except that the last
position is connected to the first position in a circular queue that
forms a circle.
Circular Queue Operations
Every queue includes two pointers, FRONT and REAR, which indicate the
position where deletion and insertion can be performed. Initially, values of
FRONT and REAR is set to -1
The circular queue is full if any of the following condition is met,
• FRONT = 0 and REAR = Max – 1
• FRONT = REAR + 1
Enqueue Operation
Inserting a new element in the position pointed by REAR is called the
enqueue operation.
• Print overflow and exit if the queue is full.
• For adding first element make FRONT = 0.
• Increment REAR by 1.
• Insert new element in the position pointed by REAR.
Dequeue Operation
Deleting an element in the position pointed by FRONT is called the
dequeue operation. We must check if the queue is empty before
deletion.
o Print underflow and exit if the queue is empty.
o Return the value pointed by FRONT.
o Increment FRONT by 1 circularly.
o Set FRONT = REAR = -1 for the last element.
Let's understand the enqueue and dequeue operation through the
diagrammatic representation.