0% found this document useful (0 votes)
8 views29 pages

DS Unit-2

A Stack is a linear data structure that operates on the LIFO principle, allowing insertion and deletion of elements only from the top. Key operations include push, pop, isEmpty, isFull, peek, and display, which can be implemented using either arrays or linked lists. Stacks are commonly used for tasks such as converting infix expressions to postfix notation.

Uploaded by

mannnybabe3307
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)
8 views29 pages

DS Unit-2

A Stack is a linear data structure that operates on the LIFO principle, allowing insertion and deletion of elements only from the top. Key operations include push, pop, isEmpty, isFull, peek, and display, which can be implemented using either arrays or linked lists. Stacks are commonly used for tasks such as converting infix expressions to postfix notation.

Uploaded by

mannnybabe3307
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/ 29

What is a Stack?

A Stack is a linear data structure that follows the LIFO (Last-In-First-Out) principle. It
contains only one pointer top pointer pointing to the topmost element of the stack. Whenever
an element is added in the stack, it is added on the top of the stack, and the element can be
deleted only from the stack. In other words, 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.

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 It is a data structure that follows some order to insert and delete the elements, and that
order can be LIFO or FILO.

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(): It returns the element at the given position.
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.

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.
Array implementation of Stack

In array implementation, the stack is formed by using the array. All the operations regarding
the stack are performed using arrays. Lets see how each operation can be implemented on the
stack using array data structure.

Adding an element onto the stack (push operation)

Adding an element into the top of the stack is referred to as push operation. Push operation
involves following two steps.

1. Increment the variable Top so that it can now refere to the next memory location.
2. Add element at the position of incremented top. This is referred to as adding new
element at the top of the stack.

Stack is overflown when we try to insert an element into a completely filled stack therefore,
our main function must always avoid stack overflow condition.

Algorithm:

void push (int val,int n) //n is size of the stack

if (top == n )

printf("\n Overflow");
else

top = top +1;

stack[top] = val;

Deletion of an element from a stack (Pop operation)

Deletion of an element from the top of the stack is called pop operation. The value of the
variable top will be incremented by 1 whenever an item is deleted from the stack. The top
most element of the stack is stored in an another variable and then the top is decremented by
1. the operation returns the deleted value that was stored in another variable as the result.

The underflow condition occurs when we try to delete an element from an already empty
stack.

int pop ()

if(top == -1)

printf("Underflow");

return 0;

else

return stack[top - - ];

Visiting each element of the stack (Peek operation)


Peek operation involves returning the element which is present at the top of the stack without
deleting it. Underflow condition can occur if we try to return the top element in an already
empty stack.

int peek()

if (top == -1)

printf("Underflow");

return 0;

else

return stack [top];

Display elements in stack:

void show()

for (i=top;i>=0;i--)

printf("%d\n",stack[i]);

if(top == -1)

printf("Stack is empty");

} }
Linked list implementation of stack

Instead of using array, we can also use linked list to implement stack. Linked list allocates the
memory dynamically. However, time complexity in both the scenario is same for all the
operations i.e. push, pop and peek.

In linked list implementation of stack, the nodes are maintained non-contiguously in the
memory. Each node contains a pointer to its immediate successor node in the stack. Stack is
said to be overflow if the space left in the memory heap is not enough to create a node.

The top most node in the stack always contains null in its address field. Lets discuss the way
in which, each operation is performed in linked list implementation of stack.

Adding a node to the stack (Push operation)

Adding a node to the stack is referred to as push operation. Pushing an element to a stack in
linked list implementation is different from that of an array implementation. In order to push
an element onto the stack, the following steps are involved.

1. Create a node first and allocate memory to it.


2. If the list is empty then the item is to be pushed as the start node of the list. This
includes assigning value to the data part of the node and assign null to the address part
of the node.
3. If there are some nodes in the list already, then we have to add the new element in the
beginning of the list (to not violate the property of the stack). For this purpose, assign
the address of the starting element to the address field of the new node and make the
new node, the starting node of the list.

void push ()

int val;

struct node *ptr =(struct node*)malloc(sizeof(struct node));

if(ptr == NULL)

printf("not able to push the element");

else
{

printf("Enter the value");

scanf("%d",&val);

if(head==NULL)

ptr->val = val;

ptr -> next = NULL;

head=ptr;

else

ptr->val = val;

ptr->next = head;

head=ptr;

printf("Item pushed");

Deleting a node from the stack (POP operation)

Deleting a node from the top of stack is referred to as pop operation. Deleting a node
from the linked list implementation of stack is different from that in the array
implementation. In order to pop an element from the stack, we need to follow the
following steps :

1. Check for the underflow condition: The underflow condition occurs when
we try to pop from an already empty stack. The stack will be empty if the head
pointer of the list points to null.
2. Adjust the head pointer accordingly: In stack, the elements are popped only
from one end, therefore, the value stored in the head pointer must be deleted
and the node must be freed. The next node of the head node now becomes the
head node.

void pop()

int item;

struct node *ptr;

if (head == NULL)

printf("Underflow");

else

item = head->val;

ptr = head;

head = head->next;

free(ptr);

printf("Item popped");

Display the nodes (Traversing)

Displaying all the nodes of a stack needs traversing all the nodes of the linked list
organized in the form of stack. For this purpose, we need to follow the following
steps.

1. Copy the head pointer into a temporary pointer.


2. Move the temporary pointer through all the nodes of the list and print the
value field attached to every node.

void display()

int i;

struct node *ptr;

ptr=head;

if(ptr == NULL)

printf("Stack is empty\n");

else

printf("Printing Stack elements \n");

while(ptr!=NULL)

printf("%d\n",ptr->val);

ptr = ptr->next;

Applications of Stack:

Transforming Infix Expressions into Postfix Expressions:

What is infix notation?

When the operator is written in between the operands, then it is known as infix notation.
Syntax of infix notation is given below:

<operand> <operator> <operand>

In the algebraic expression, the order of the operator precedence is given in the below table:

Operators Symbols

Parenthesis ( ), {}, [ ]

Exponents ^

Multiplication and Division *, /

Addition and Subtraction +,-

The operators that have the same precedence termed as operator associativity. If we go from
left to right, then it is known as left-associative. If we go from right to left, then it is known as
right-associative.

Problem with infix notation

To evaluate the infix expression, we should know about the operator precedence rules, and
if the operators have the same precedence, then we should follow the associativity rules. The
use of parenthesis is very important in infix notation to control the order in which the
operation to be performed. Parenthesis improves the readability of the expression. An infix
expression is the most common way of writing expression, but it is not easy to parse and
evaluate the infix expression without ambiguity. So, mathematicians and logicians studied
this problem and discovered two other ways of writing expressions which are prefix and
postfix. Both expressions do not require any parenthesis and can be parsed without
ambiguity. It does not require operator precedence and associativity rules.

Postfix Expression

The postfix expression is an expression in which the operator is written after the operands.
For example, the postfix expression of infix notation ( 2+3) can be written as 23+.

Conversion of infix to postfix

Here, we will use the stack data structure for the conversion of infix expression to prefix
expression. Whenever an operator will encounter, we push operator into the stack. If we
encounter an operand, then we append the operand to the expression.
Rules for the conversion from infix to postfix expression

1. Print the operand as they arrive.


2. If the stack is empty or contains a left parenthesis on top, push the incoming operator
on to the stack.
3. If the incoming symbol is '(', push it on to the stack.
4. If the incoming symbol is ')', pop the stack and print the operators until the left
parenthesis is found.
5. If the incoming symbol has higher precedence than the top of the stack, push it on the
stack.
6. If the incoming symbol has lower precedence than the top of the stack, pop and print
the top of the stack. Then test the incoming operator against the new top of the stack.
7. If the incoming operator has the same precedence with the top of the stack then use
the associativity rules. If the associativity is from left to right then pop and print the
top of the stack then push the incoming operator. If the associativity is from right to
left then push the incoming operator.
8. At the end of the expression, pop and print all the operators of the stack.

Let's understand through an example.

1) Infix expression: K + L - M*N + (O^P) * W/U/V * T + Q

Input Expression Stack Postfix Expression

K K

+ +

L + KL

- - K L+

M - K L+ M

* -* K L+ M

N -* KL+MN

+ + K L + M N*
K L + M N* -

( +( K L + M N *-
O +( KL+MN*-O

^ +(^ K L + M N* - O

P +(^ K L + M N* - O P

) + K L + M N* - O P ^

* +* K L + M N* - O P ^

W +* K L + M N* - O P ^ W

/ +/ K L + M N* - O P ^ W *

U +/ K L + M N* - O P ^W*U

/ +/ K L + M N* - O P ^W*U/

V +/ KL + MN*-OP^W*U/V

* +* KL+MN*-OP^W*U/V/

T +* KL+MN*-OP^W*U/V/T

+ + KL+MN*-OP^W*U/V/T*
KL+MN*-OP^W*U/V/T*+

Q + KL+MN*-OP^W*U/V/T*Q

KL+MN*-OP^W*U/V/T*+Q+

The final postfix expression of infix expression(K + L - M*N + (O^P) * W/U/V * T + Q) is


KL+MN*-OP^W*U/V/T*+Q+.

2) Input: ((A + B) – C * (D / E)) + F


Output: AB+CDE/*-F+

3. Infix Expression: A+(B*C-(D/E^F)*G)*H, where ^ is an exponential operator

Input String Output Stack Operator Stack


Input String Output Stack Operator Stack

A+(B*C-(D/E^F)*G)*H

A+(B*C-(D/E^F)*G)*H A

A+(B*C-(D/E^F)*G)*H A +

A+(B*C-(D/E^F)*G)*H A +(

A+(B*C-(D/E^F)*G)*H AB +(

A+(B*C-(D/E^F)*G)*H AB +(*

A+(B*C-(D/E^F)*G)*H ABC +(*

A+(B*C-(D/E^F)*G)*H ABC* +(-

A+(B*C-(D/E^F)*G)*H ABC* +(-(

A+(B*C-(D/E^F)*G)*H ABC*D +(-(

A+(B*C-(D/E^F)*G)*H ABC*D +(-(/

A+(B*C-(D/E^F)*G)*H ABC*DE +(-(/

A+(B*C-(D/E^F)*G)*H ABC*DE +(-(/^


Input String Output Stack Operator Stack

A+(B*C-(D/E^F)*G)*H ABC*DEF +(-(/^

A+(B*C-(D/E^F)*G)*H ABC*DEF^/ +(-

A+(B*C-(D/E^F)*G)*H ABC*DEF^/ +(-*

A+(B*C-(D/E^F)*G)*H ABC*DEF^/G +(-*

A+(B*C-(D/E^F)*G)*H ABC*DEF^/G*- +

A+(B*C-(D/E^F)*G)*H ABC*DEF^/G*- +*

A+(B*C-(D/E^F)*G)*H ABC*DEF^/G*-H +*

A+(B*C-(D/E^F)*G)*H ABC*DEF^/G*-H*+

4. Convert the following infix expression to postfix form


A–(B/C+ (D%E*F)/G)*H
Solution – Infix to Postfix conversion of the above question is explained in the following
image.
5)

ANS: PQ+RS+*T/ABC+*+
6)

7. Input: A*B+C
Output: AB*C+

Input: (A+B)*(C/D)
Output: AB+CD/*

Input: A*(B*C+D*E)+F
Output: ABC*DE*+*F+

Input: (A+B)*C+(D-E)/F+G
Output: AB+C*DE-F/+G+

Evaluation of a Postfix Expression

Postfix Evaluation Algorithm

Postfix evaluation algorithm is a simple algorithm that allows us to evaluate postfix


expressions. The algorithm uses a stack to keep track of operands and performs arithmetic
operations when an operator is encountered. The algorithm can be summarized in the
following steps:

1. First of all, it will Create an empty stack.


2. After that, it Scan the expression from left to right.
3. If an operand is encountered, it push it onto the stack.
4. If an operator is encountered, pop the top two operands from the stack, perform the
operation, and push the result back onto the stack.
5. After that, it Continue scanning the expression until all tokens have been processed.
6. When the expression has been fully scanned, the result will be the top element of the
stack.

Evaluate the Given Postfix Expression 6 5 2 3 + 8 * + 3 + *


Solution
Step wise step solution of the above questions is given below

Evaluate the value of following postfix expression


623+–382/+*2^3+
Solution – Solution of the above question is given below
Queue

1. A queue can be defined as an ordered list which enables insert operations to be performed
at one end called REAR and delete operations to be performed at another end
called FRONT.

2. Queue is referred to be as First In First Out list.

3. 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 out basis which is quite fair for the
ordering of actions. There 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. Queue are used to maintain the play list in media players in order to add and remove
the songs from the play-list.
5. Queues are used in operating systems for handling interrupts.

Types of Queue

There are four different types of queue that are listed as follows -

o Simple Queue or Linear Queue


o Circular Queue
o Priority Queue
o Double Ended Queue (or Deque)

Let's discuss each of the type of queue.

Simple Queue or Linear Queue

In Linear Queue, an insertion takes place from one end while the deletion occurs from
another end. The end at which the insertion takes place is known as the rear end, and the end
at which the deletion takes place is known as front end. It strictly follows the FIFO rule.

The major drawback of using a linear Queue is that insertion is done only from the rear end.
If the first three elements are deleted from the Queue, we cannot insert more elements even
though the space is available in a Linear Queue. In this case, the linear Queue shows the
overflow condition as the rear is pointing to the last element of the Queue.

Circular Queue

In Circular Queue, all the nodes are represented as circular. It is similar to the linear Queue
except that the last element of the queue is connected to the first element. It is also known as
Ring Buffer, as all the ends are connected to another end. The representation of circular
queue is shown in the below image -

The drawback that occurs in a linear queue is overcome by using the circular queue. If the
empty space is available in a circular queue, the new element can be added in an empty space
by simply incrementing the value of rear. The main advantage of using the circular queue is
better memory utilization.

Priority Queue
It is a special type of queue in which the elements are arranged based on the priority. It is a
special type of queue data structure in which every element has a priority associated with it.
Suppose some elements occur with the same priority, they will be arranged according to the
FIFO principle. The representation of priority queue is shown in the below image -

Insertion in priority queue takes place based on the arrival, while deletion in the priority
queue occurs based on the priority. Priority queue is mainly used to implement the CPU
scheduling algorithms.

There are two types of priority queue that are discussed as follows -

o Ascending priority queue - In ascending priority queue, elements can be inserted in


arbitrary order, but only smallest can be deleted first. Suppose an array with elements
7, 5, and 3 in the same order, so, insertion can be done with the same sequence, but
the order of deleting the elements is 3, 5, 7.
o Descending priority queue - In descending priority queue, elements can be inserted
in arbitrary order, but only the largest element can be deleted first. Suppose an array
with elements 7, 3, and 5 in the same order, so, insertion can be done with the same
sequence, but the order of deleting the elements is 7, 5, 3.

Deque (or, Double Ended Queue)

In Deque or Double Ended Queue, insertion and deletion can be done from both ends of the
queue either from the front or rear. It means that we can insert and delete elements from both
front and rear ends of the queue. Deque can be used as a palindrome checker means that if we
read the string from both ends, then the string would be the same.

Deque can be used both as stack and queue as it allows the insertion and deletion operations
on both ends. Deque can be considered as stack because stack follows the LIFO (Last In First
Out) principle in which insertion and deletion both can be performed only from one end. And
in deque, it is possible to perform both insertion and deletion from one end, and Deque does
not follow the FIFO principle.

The representation of the deque is shown in the below image -


There are two types of deque that are discussed as follows -

o Input restricted deque - As the name implies, in input restricted queue, insertion
operation can be performed at only one end, while deletion can be performed from
both ends.

o Output restricted deque - As the name implies, in output restricted queue, deletion
operation can be performed at only one end, while insertion can be performed from
both ends.

Now, let's see the operations performed on the queue.

Operations performed on queue

The fundamental operations that can be performed on queue are listed as follows -
o Enqueue: The Enqueue operation is used to insert the element at the rear end of the
queue. It returns void.
o Dequeue: It performs the deletion from the front-end of the queue. It also returns the
element which has been removed from the front-end. It returns an integer value.
o Peek: This is the third operation that returns the element, which is pointed by the
front pointer in the queue but does not delete it.
o Queue overflow (isfull): It shows the overflow condition when the queue is
completely full.
o Queue underflow (isempty): It shows the underflow condition when the Queue is
empty, i.e., no elements are in the Queue.

Now, let's see the ways to implement the queue.

Ways to implement the queue

There are two ways of implementing the Queue:

o Implementation using array: The sequential allocation in a Queue can be


implemented using an array.
o Implementation using Linked list: The linked list allocation in a Queue can be
implemented using a linked list.

Array representation of Queue

We can easily represent queue by using linear arrays. There are two variables i.e. front and
rear, that are implemented in the case of every queue. Front and rear variables point to the
position from where insertions and deletions are performed in a queue. Initially, the value of
front and queue is -1 which represents an empty queue. Array representation of a queue
containing 5 elements along with the respective values of front and rear, is shown in the
following figure.
The above figure shows the queue of characters forming the English word "HELLO". Since,
No deletion is performed in the queue till now, therefore the value of front remains -1 .
However, the value of rear increases by one every time an insertion is performed in the
queue. After inserting an element into the queue shown in the above figure, the queue will
look something like following. The value of rear will become 5 while the value of front
remains same.

After deleting an element, the value of front will increase from -1 to 0. however, the queue
will look something like following.
Algorithm to insert any element in a queue

Check if the queue is already full by comparing rear to max - 1. if so, then return an overflow
error.

If the item is to be inserted as the first element in the list, in that case set the value of front
and rear to 0 and insert the element at the rear end.

Otherwise keep increasing the value of rear and insert each element one by one having rear as
the index.

Algorithm
o Step 1: IF REAR = MAX - 1
Write OVERFLOW
Go to step
[END OF IF]
o Step 2: IF FRONT = -1 and REAR = -1
SET FRONT = REAR = 0
ELSE
SET REAR = REAR + 1
[END OF IF]
o Step 3: Set QUEUE[REAR] = NUM
o Step 4: EXIT

Algorithm to delete an element from the queue

If, the value of front is -1 or value of front is greater than rear , write an underflow message
and exit.
Otherwise, keep increasing the value of front and return the item stored at the front end of the
queue at each time.

Algorithm
o Step 1: IF FRONT = -1 or FRONT > REAR
Write UNDERFLOW
ELSE
SET VAL = QUEUE[FRONT]
SET FRONT = FRONT + 1
[END OF IF]
o Step 2: EXIT

Drawback of array implementation

Although, the technique of creating a queue is easy, but there are some drawbacks of using
this technique to implement a queue.

o Memory wastage : The space of the array, which is used to store queue elements, can
never be reused to store the elements of that queue because the elements can only be
inserted at front end and the value of front might be so high so that, all the space
before that, can never be filled.

The above figure shows how the memory space is wasted in the array representation of
queue. In the above figure, a queue of size 10 having 3 elements, is shown. The value of the
front variable is 5, therefore, we can not reinsert the values in the place of already deleted
element before the position of front. That much space of the array is wasted and can not be
used in the future (for this queue).

o Deciding the array size

On of the most common problem with array implementation is the size of the array which
requires to be declared in advance. Due to the fact that, the queue can be extended at runtime
depending upon the problem, the extension in the array size is a time taking process and
almost impossible to be performed at runtime since a lot of reallocations take place. Due to
this reason, we can declare the array large enough so that we can store queue elements as
enough as possible but the main problem with this declaration is that, most of the array slots
(nearly half) can never be reused. It will again lead to memory wastage.

Linked List implementation of Queue

the array implementation can not be used for the large scale applications where the queues
are implemented. One of the alternative of array implementation is linked list implementation
of queue.

The storage requirement of linked representation of a queue with n elements is o(n) while the
time requirement for operations is o(1).

In a linked queue, each node of the queue consists of two parts i.e. data part and the link part.
Each element of the queue points to its immediate next element in the memory.

In the linked queue, there are two pointers maintained in the memory i.e. front pointer and
rear pointer. The front pointer contains the address of the starting element of the queue while
the rear pointer contains the address of the last element of the queue.

Insertion and deletions are performed at rear and front end respectively. If front and rear both
are NULL, it indicates that the queue is empty.

The linked representation of queue is shown in the following figure.

Operation on Linked Queue

There are two basic operations which can be implemented on the linked queues. The
operations are Insertion and Deletion.

Insert operation

The insert operation append the queue by adding an element to the end of the queue. The new
element will be the last element of the queue.
Firstly, allocate the memory for the new node ptr by using the following statement.

1. Ptr = (struct node *) malloc (sizeof(struct node));

There can be the two scenario of inserting this new node ptr into the linked queue.

In the first scenario, we insert element into an empty queue. In this case, the condition front
= NULL becomes true. Now, the new element will be added as the only element of the queue
and the next pointer of front and rear pointer both, will point to NULL.

ptr -> data = item;


if(front == NULL)
{
front = ptr;
rear = ptr;
front -> next = NULL;
rear -> next = NULL;
}

In the second case, the queue contains more than one element. The condition front = NULL
becomes false. In this scenario, we need to update the end pointer rear so that the next pointer
of rear will point to the new node ptr. Since, this is a linked queue, hence we also need to
make the rear pointer point to the newly added node ptr. We also need to make the next
pointer of rear point to NULL.

rear -> next = ptr;


rear = ptr;
rear->next = NULL;

Deletion

Deletion operation removes the element that is first inserted among all the queue elements.
Firstly, we need to check either the list is empty or not. The condition front == NULL
becomes true if the list is empty, in this case , we simply write underflow on the console and
make exit.

Otherwise, we will delete the element that is pointed by the pointer front. For this purpose,
copy the node pointed by the front pointer into the pointer ptr. Now, shift the front pointer,
point to its next node and free the node pointed by the node ptr. This is done by using the
following statements.

ptr = front;
front = front -> next;
free(ptr);

You might also like