Unit II
Unit II
A Stack is a linear data structure that follows the LIFO (Last-In-First-Out) principle.
Stack has one end, 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 top of 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
Stack Abstract Data Types (ADT):
The following are some common operations implemented on the stack:
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.
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.
isEmpty(): It determines whether the stack is empty or not.
isFull(): It determines whether the stack is full or not.'
peek(): It returns the element at the given position.
count(): It returns the total number of elements available in a stack.
display(): It prints all the elements available in the stack.
Application of the Stack:
A Stack can be used for evaluating expressions consisting of operands and operators.
Stacks can be used for Backtracking, i.e., to check parenthesis matching in an expression.
It can also be used to convert one form of expression to another form.
Checking well-formed (nested) parenthesis
Processing function calls
In computations such as decimal to binary conversion
It can be used for systematic Memory Management.
Representation of Stacks through Arrays:
PUSH:
PUSH operation implies the insertion of a new element into a Stack.
A new element is always inserted from the topmost position of the Stack; thus, we always
need to check if the top is empty or not, i.e., TOP=Max-1 if this condition goes false, it means
the Stack is full, and no more elements can be inserted, and even if we try to insert the
element, a Stack overflow message will be displayed.
Algorithm:
Step-1: If TOP = Max-1
Print “Overflow”
Goto Step 4
Step-2: Set TOP= TOP + 1
Step-3: Set Stack[TOP]= ELEMENT
Step-4: END
POP:
POP means to delete an element from the Stack. Before deleting an element, make sure to
check if the Stack Top is -1, i.e., TOP=-1. If this condition goes true, it means the Stack is
empty, and no deletion operation can be performed, and even if we try to delete, then the
Stack underflow message will be generated.
Algorithm:
Step-1: If TOP= -1
Print “Underflow”
Goto Step 4
Step-2: Set VAL= Stack[TOP]
Step-3: Set TOP= TOP-1
Step-4: END
PEEK:
When we need to return the value of the topmost element of the Stack without deleting it from
the Stack, the Peek operation is used.
This operation first checks if the Stack is empty, i.e., TOP = -1; if it is so, then an appropriate
message will display, else the value will return.
Algorithm:
Step-1: If TOP = -1
PRINT “Stack is Empty”
Goto Step 3
Step-2: Return Stack[TOP]
Step-3: END
Stack Program :
#include<iostream>
#define MAX 10
using namespace std;
class Stack{
int top;
int stack[MAX];
public:
Stack()
{
top=-1;
}
void push(int ele);
int pop();
int isFull();
int isEmpty();
void display();
int getElement(int ele);
};
void Stack :: push(int ele)
{
if(top==MAX-1){
cout<<"Stack is Overflow";
}
else
{
stack[++top]=ele;
cout<<"\n Element Pushed into Stack!!";
}
}
int Stack :: pop()
{
if(top==-1)
{
cout<<"Stack is Underflow";
}
else
{
cout<<stack[top]<<" Element Pop";
top--;
}
}
if(!isEmpty()){
cout<<stack[ele] << " From the given index";
}
else
{
cout<<"Stack is Empty";
}
}
else
{
cout<<"Invalid Index";
}
}
int main()
{
int ch,ele;
Stack ob;
do{
} while(ch<7);
}
Q2) EXPRESSION EVALUATI ON AND CONVERSION?
The most frequent application of stacks is in the evaluation of arithmetic expressions.
An arithmetic expression is made of operands, operators, and delimiters.
For instance, consider the following expression:
X = a/b /c – d
Let a = 1, b = 2, c = 3, and d = 4.
One of the meanings that can be drawn from this expression could be
X = (1/2) / (3 - 4) = -1/2
Another way to evaluate the same expression could be
X = (1/(2 \ 3)) - 4 = -23/6
To avoid more than one meaning being drawn out of an expression, we have to specify
the order of operation by using parentheses. For instance,
X = (a/b) / (c - d)
To fix the order of evaluation, assign each operator a priority. Even though we write
the expression in parentheses, we still query whether to evaluate (A/B) first or to evaluate
(C - D) first. Once the priorities are assigned, then within any pairs of parentheses the
Operators with the highest priority are to be evaluated first. While evaluating an expression,
the following operation precedence is usually used:
The following operators are written in descending order of their precedence:
1. Exponentiation (^), Unary (+), Unary (-), and not (~)
2. Multiplication (*) and division (/)
3. Addition (+) and subtraction (-)
4. Relational operators <,<= , =,!=, π, =, >
5. Logical AND(&&)
6. Logical OR ( | | )
Note that all the relational operators have the same priority. Exponentiation (^) and unary
operators (+, -, and ~) have the highest priority.
When there are two adjacent operators with the same priority(A ^ B ^ C), then the expression
is to be evaluated from right to left.
Example:
2 ^ 3 ^ 2 evaluate it as 2 ^ (3 ^ 2).
When evaluated from left to right, the expression may be evaluated as ((2 ^ 3) ^ 2), which is
wrong!
Expressions such as A + B - C and A * B/C are to be evaluated from left to right.
the operators need to decide on a rule for proceeding from left to right for all expressions
except the operator exponential. This order of evaluation, from left to right or right to left, is
called associativity. Exponentiation is right associative and all other operators are left
associative
Example : 23^2
Q 3) Polish Notation and Expression Conversion
This notation was invented by the Polish mathematician Jan Łukasiewicz in the 1920s.
which gives two alternatives to represent an arithmetic expression, namely the postfix and
prefix notations.
The fundamental property of Polish notation is that the order in which the operations are to be
performed is determined by the positions of the operators and operands in the expression.
the advantage is that parentheses is not required while writing expressions in Polish notation.
The conventional way of writing the expression
is called infix, because the binary operators occur between the operands, and unary operators
precede their operand.
For example, the expression ((A + B) * C)/D is an infix expression
In postfix notation, the operator is written after its operands A B + C * D /
in prefix notation, the operator precedes its operands /*+ABC
Q4) Infix to Postfix evaluation using stack ?
Algorithm :
Scan all the symbols one by one from left to right in the given Infix Expression.
If the reading symbol is an operand, then immediately append it to the Postfix Expression.
If the reading symbol is left parenthesis ‘( ‘, then Push it onto the Stack.
If the reading symbol is right parenthesis ‘)’, then Pop all the contents of the stack until the
respective left parenthesis is popped and append each popped symbol to Postfix Expression.
If the reading symbol is an operator (+, –, *, /), then Push it onto the Stack. However, first,
pop the operators which are already on the stack that have higher or equal precedence than the
current operator and append them to the postfix. If an open parenthesis is there on top of the
stack then push the operator into the stack.
If the input is over, pop all the remaining symbols from the stack and append them to the
postfix.
Example :
#include<iostream>
#include<stack>
using namespace std;
int main() {
string infix;
cout << "Enter infix expression: ";
cin >> infix;
string postfix = infixToPostfix(infix);
cout << "Postfix expression: " << postfix << endl;
return 0;
}
Example :
#include<iostream>
#include<stack>
#include <bits/stdc++.h>
using namespace std;
}
s.push(c);
}
}
}
while(!s.empty()) {
postfix += s.top();
s.pop();
}
return postfix;
}
if (infix[i] == '(') {
infix[i] = ')';
}
else if (infix[i] == ')') {
infix[i] = '(';
}
}
prefix=infixToPostfix(infix);
reverse(prefix.begin(), prefix.end());
return prefix;
}
int main() {
string infix;
cout << "Enter infix expression: ";
cin >> infix;
string prefix = infixToPrefix(infix);
cout << "Prefix expression: " << prefix << endl;
return 0;
}
Q7) Evaluation of Postfix Expression using Stack
exp = “2 3 1 * + 9 -“
Step-1 : Scan 2, it’s a number, So push it into stack. Stack contains ‘2’.
Step-5 : Scan +, it’s an operator. Pop two operands from stack, apply the + operator on
operands. We get 3 + 2 which results in 5. We push the result 5 to stack. The stack now
becomes ‘5’.
Step-6 : Scan 9, it’s a number. So we push it to the stack. The stack now becomes ‘5 9’.
Step-7 : Scan -, it’s an operator, pop two operands from stack, apply the – operator on
operands, we get 5 – 9 which results in -4. We push the result -4 to the stack. The stack now
becomes ‘-4’.
Example :
#include <bits/stdc++.h>
using namespace std;
if (isdigit(exp[i])) {
st.push(exp[i] - '0');
}
else {
int val1 = st.top();
st.pop();
int val2 = st.top();
st.pop();
switch (exp[i]) {
case '+':
st.push(val2 + val1);
break;
case '-':
st.push(val2 - val1);
break;
case '*':
st.push(val2 * val1);
break;
case '/':
st.push(val2 / val1);
break;
}
}
}
return st.top();
}
int main()
{
string exp = "231*+9-";
The queue abstract data type (ADT) follows the basic design of the stack abstract data type.
add – Insert an element at the end of the queue.
delete – Remove and return the first element of the queue, if the queue is not empty.
getFront() – Return the element of the queue without removing it, if the queue is not empty.
size() – Return the number of elements in the queue.
isEmpty() – Return true if the queue is empty, otherwise return false.
isFull() – Return true if the queue is full, otherwise return false.
Queue Operations using Array
Queue data structure using array can be implemented as follows...
Before we implement actual operations, first follow the below steps to create an empty queue.
Step 1 - Include all the header files which are used in the program and define a
constant 'SIZE' with specific value.
Step 2 - Create a one dimensional array with above defined SIZE (int queue[SIZE])
Step 3 - Define two integer variables 'front' and 'rear' and initialize both with '-1'. (int front
= -1, rear = -1)
Step 4 - Then implement main method by displaying menu of operations list and make
suitable function calls to perform operation selected by the user on queue.
add(value) : Inserting value into the queue
In a queue data structure, add() is a function used to insert a new element into the queue. In a
queue, the new element is always inserted at rear position. The add() function takes one integer
value as a parameter and inserts that value into the queue. We can use the following steps to insert
an element into the queue...
Step 1 - Check whether queue is FULL. (rear == SIZE-1)
Step 2 - If it is FULL, then display "Queue is FULL!!! Insertion is not possible!!!" and
terminate the function.
Step 3 - If it is NOT FULL, then increment rear value by one (rear++) and
set queue[rear] = value.
delete() : Deleting a value from the Queue
In a queue data structure, delete() is a function used to delete an element from the queue. In a
queue, the element is always deleted from front position. The delete() function does not take any
value as parameter. We can use the following steps to delete an element from the queue...
Step 1 - Check whether queue is EMPTY. (front == rear)
Step 2 - If it is EMPTY, then display "Queue is EMPTY!!! Deletion is not possible!!!" and
terminate the function.
Step 3 - If it is NOT EMPTY, then increment the front value by one (front ++). Then
display queue[front] as deleted element. Then check whether both front and rear are equal
(front == rear), if it TRUE, then set both front and rear to '-1' (front = rear = -1).
Display(): Displays the elements of a Queue
We can use the following steps to display the elements of a queue...
Step 1 - Check whether queue is EMPTY. (front == rear)
Step 2 - If it is EMPTY, then display "Queue is EMPTY!!!" and terminate the function.
Step 3 - If it is NOT EMPTY, then define an integer variable 'i' and set 'i = front+1'.
Step 4 - Display 'queue[i]' value and increment 'i' value by one (i++). Repeat the same until 'i'
value reaches to rear (i <= rear)
Example :
#include<iostream>
using namespace std;
class queue
{
private:
int Rear, Front;
int Queue[7];
int max;
public:
queue()
{
max = 7;
Rear = Front = -1 ;
}
int Is_Empty();
int Is_Full();
void Add(int Element);
int Delete();
int getFront();
void display();
};
void queue :: display()
{
if(Rear!=-1) {
}
int queue :: Delete()
{
if(!Is_Empty())
{
return(Queue[++Front]);
}
else
{
cout<< "Queue is Empty";
}
}
int queue :: getFront()
{
if(!Is_Empty())
return(Queue[Front + 1]);
else
cout<<"Queue is Empty";
}
int main()
{
int ch,ele;
queue ob;
do{
case 5:
ob.display(); break;
case 6:
cout<<"\n Program End!!!"; break;
default: cout<<"\n invalid Choice!!";
} while(ch<6);
}
Q9) Realization of queues using Arrays?
We already know that an array is not a suitable data structure for frequent insertion and
deletion of data elements.
Another drawback of arrays is that they use static memory allocation, and so they can store
only a fixed number of elements.
Queues have a limited capacity, and once that capacity is reached, new elements cannot be
added until existing elements are removed. This limitation can lead to a situation where the
queue is full, but new elements need to be added, resulting in overflow and data loss.
There are two solutions to this problem: one is using a circular queue and the other is using a
linked organization for realization of the queue..
Step 4: EXIT
Program :
#include<bits/stdc++.h>
using namespace std;
class cQueue
{
int rear, front;
int size;
int *arr;
public:
cQueue(int s)
{
front = rear = -1;
size = s;
arr = new int[s];
}
void enQueue(int value);
int deQueue();
void displayQueue();
};
void cQueue::enQueue(int value)
{
if ((front == 0 && rear == size-1) || (rear == (front-1)%(size-1)))
{
printf("\nQueue is Full");
return;
}
else if (front == -1)
{
front = rear = 0;
arr[rear] = value;
}
else if (rear == size-1 && front != 0)
{
rear = 0;
arr[rear] = value;
}
else
{
rear++;
arr[rear] = value;
}
}
int cQueue::deQueue()
{
if (front == -1)
{
printf("\nQueue is Empty");
return 0;
}
return data;
}
void cQueue::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]);
return 0;
}
Program :
#include <iostream>
#include <queue>
using namespace std;
int main()
{
int arr[6] = { 10, 2, 4, 8, 6, 9 };
priority_queue<int> pq;
return 0;
}