Dsa Unit-2
Dsa Unit-2
Stacks:
Stacks, Stacks using Arrays
Stacks using dynamic arrays
Evaluation of Expressions – Evaluating Postfix Expression,
Evaluation of Expressions -Infix to Postfix
Queues:
Queues ADT
Queues operations
Circular Queues
Queues Applications
WHAT IS STACK:
• A Stack is a linear data structure that follows the LIFO (Last-In-First-
Out) principle. Stack has one end, whereas the Queue has two ends (front and
rear).
• 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.
• Operations on stack:The two basic operations associated with stacks
are:
1.Push
2.Pop
1.Push: Push operation is used to add new elements in to the stack. At the time
of addition first check the stack is full or not. If the stack is full it generates an
message is "stack overflow".
2.Pop: Pop operation is used to delete elements from the stack. At the time of
deletion first check the stack is empty or not. If the stack is empty it generates an
message "stack underflow".
•All insertions and deletions elements take place at the same end(called
top of stack), so the last element added to the stack will be the first element
removed from the stack.
newNode->next=top;
• Algorithm for push()
• If the top is equal to NULL
newNode -> next=NULL
Else
newNode -> next=top
if top == NULL
Stack Underflow"
else
create temporary node,
*temp = top
create temporary variable,
temp_data = top->data
top = top->next
free(temp)
return
temp_data
Example of Pop() Operation: //Structure to create a node with data and the next pointer
int pop()
{
if (top == NULL)
{
printf("\nEMPTY STACK");
}
else
{
struct Node *temp = top;
int temp_data = top->data; //to store data of top node
top = top->next;
free(temp); //deleting the node
return temp_data; }}
Program to implement Stack using Linked List(Stack using Dynamic Array)
// Structure to create a node with data and the next pointer
void main()
#include<stdio.h> {
#include<stdlib.h> int choice, value;
printf("\n:: Stack using Linked List ::\n");
struct Node while(1!=5)
{ {
printf("\n****** MENU ******\n");
int data; printf("1. Push\n2.peek\n3. Pop\n4. Display\n");
struct Node *next; printf("Enter your choice: ");
scanf("%d",&choice);
}*top = NULL; switch(choice)
void push(int); {
void peek(); case 1: printf("Enter the value to be insert: ");
scanf("%d", &value);
void pop(); push(value); break;
void display(); case 2: peek(); break;
case 3: pop(); break;
case 4: display(); break;
default: printf("\nWrong selection!!! Please try again!!!\n");
break(); } }}
void push(int value) void peek()
{ {
struct Node *newNode; if(top == NULL)
newNode= (struct {
Node*)malloc(sizeof(struct Node)); printf("\nStack isEmpty!!!\n");
newNode->data = value; }
if(top == NULL) else
newNode->next = NULL; {
else printf("\ntop of element=%d",top-
newNode->next = top; >data);
top = newNode; }}
printf("\nInsertion is Success!!!\n");}
void pop() void display()
{ {
if(top == NULL)
if(top == NULL)
printf("\nStack is Empty!!!\n");
printf("\nStack is Empty!!!\n"); else
else
{
{ struct Node *temp = top;
struct Node *temp = top; while(temp->next != NULL)
printf("\nDeleted element: %d", {
temp->data); printf("%d--->",temp->data);
temp = temp -> next;
top = temp->next;
}
free(temp); printf("%d--->NULL",temp->data);
} }
} }
• Advantages Of Linked List:
• Dynamic data structure: A linked list is a dynamic arrangement so it can grow and shrink
at runtime by allocating and deallocating memory. So there is no need to give the initial
size of the linked list.
• No memory wastage: In the Linked list, efficient memory utilization can be achieved since
the size of the linked list increase or decrease at run time so there is no memory wastage
and there is no need to pre-allocate the memory.
• Implementation: Linear data structures like stacks and queues are often easily
implemented using a linked list.
• Insertion and Deletion Operations: Insertion and deletion operations are quite easier in
the linked list. There is no need to shift elements after the insertion or deletion of an
element only the address present in the next pointer needs to be updated.
• Flexible: This is because the elements in Linked List are not stored in contiguous memory
locations unlike the array.
• Efficient for large data: When working with large datasets linked lists play a crucial role
as it can grow and shrink dynamically.
• Scalability: Contains the ability to add or remove elements at any position.
• Disadvantages Of Linked List:
• Memory usage: More memory is required in the linked list as compared to an array. Because in a linked
list, a pointer is also required to store the address of the next element and it requires extra memory for
itself.
• Traversal: In a Linked list traversal is more time-consuming as compared to an array. Direct access to an
element is not possible in a linked list as in an array by index. For example, for accessing a node at
position n, one has to traverse all the nodes before it.
• Reverse Traversing: In a singly linked list reverse traversing is not possible, but in the case of a
doubly-linked list, it can be possible as it contains a pointer to the previously connected nodes with each
node. For performing this extra memory is required for the back pointer hence, there is a wastage of
memory.
• Random Access: Random access is not possible in a linked list due to its dynamic memory allocation.
• Lower efficiency at times: For certain operations, such as searching for an element or iterating through
the list, can be slower in a linked list.
• Complex implementation: The linked list implementation is more complex when compared to array. It
requires a complex programming understanding.
• Difficult to share data: This is because it is not possible to directly access the memory address of an
element in a linked list.
• Not suited for small dataset: Cannot provide any significant benefits on small dataset compare to that of
an array.
Evaluation of Expression- Infix to Postfix Conversion, Evaluation of Postfix Expression:
Algebraic expressions: An algebraic expression is a legal combination of operands and operators.
Operand is the quantity on which a mathematical operation is performed. Operand may be a variable like x, y,
z or a constant like 5, 4, 6 etc.
Operator is a symbol which signifies a mathematical or logical operation between the operands. Examples of
familiar operators include +, -, *, /, ^ etc.
An algebraic expression can be represented using three different notations. They are infix, postfix and
prefix notations:
1.Infix: It is the form of an arithmetic expression in which we fix (place) the arithmetic operator in between
the two operands.
The syntax of Infix notation is: < operand > < operator > <operand>
Example: A + B
2.Prefix: It is the form of an arithmetic notation in which we fix (place) the arithmetic operator before
(pre) its two operands. The prefix notation is called as polish notation.
The syntax of Prefix notation is: < operator > < operand > <operand>
Example: + A B
3.Postfix: It is the form of an arithmetic expression in which we fix (place) the arithmetic operator after (post)
its two operands. The postfix notation is called as suffix notation and is also referred to reverse polish notation.
The syntax of Postfix notation is: < operand > <operand>< operator >
Example: A B +
Evaluation of Expression:
• The operator with higher precedence is evaluated first and the operator with the least precedence is evaluated
last. An expression is evaluated based on the precedence and associativity of the operators in that expression.
• There are different levels of operator precedence and an operator may belong to one of these levels.
Conversion from infix to postfix:
Procedure Algorithm for Conversion of Infix to Postfix using Stack as
follows:
• 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.
• We consider five binary operations: +, -, *, / and $ or ↑ (exponentiation). For
these binary operations, the following in the order of precedence (highest to
lowest):
C Program to Convert Infix expression into Postfix expression using Stack
#include<stdio.h>
#include<ctype.h>// The ctype. h header file declares functions used in character
classification.
char stack[100];
int top = -1;
void push(char x)
{
stack[++top] = x;
}
char pop()
{
if(top == -1)
return -1;
else
return stack[top--];
}
int priority(char x)
{
if(x == '(')
return 0;
if(x == '+' || x == '-')
return 1;
if(x == '*' || x == '/')
return 2;
if(x == '^' || x == '$')
return 3;
return 0;
}
int main()
{
char exp[100];
char *e, x;
printf("Enter the Infix expression : ");
scanf("%s",exp);
printf("\n");
e = exp;
while(*e != '\0’)
{
if(isalnum(*e))
printf("%c ",*e);
else if(*e == '(')
push(*e);
else if(*e == ')')
{
while((x = pop()) != '(')
printf("%c ", x);
} else
{
while(priority(stack[top]) >= priority(*e))
printf("%c ",pop());
push(*e);
}
e++;
}
while(top != -1)
{
printf("%c ",pop());
}
return 0;
}
Output Test Case 1:
Enter the Infix expression : ((4+8)(6-5))/((3-2)(2+2))
48+65-32-22+/
Output Test Case 2:
Enter the infix expression: A+B*C/A+(B+C)
ABC*A/ +BC++
Output Test Case 3:
Enter the Infix expression : z$a/b*(c+d-f/g)
za$b/cd+fg/-*
• Conversion from infix to prefix:
The conversion from infix notation to prefix notation involves reversing the order of the Expression.
Given an infix arithmetic expression convert into an equivalent prefix expression:
• Expression will be given in the form of string , where alphabetic characters i.e a-z or A-Z denotes operands
and operators are ( + , – , * , / ) . Expression can also have brackets i.e ‘(’ and ‘)’.
• Sample example :
• Input: infix expression: a * ( b + c + d) Output: prefix expression: *a+b+cd
• Input: infix expression: b*c Output: prefix expression: *bc
Here, is the infix to prefix examples Rules:
Consider the infix expression: (3 + 4 * 2) / (1 – 5)
Convert all opening brackets to closing brackets and vice versa
Reverse the infix expression: )5 – 1(/ )2 * 4 + 3(
How to Design Infix to Prefix Algorithm:
1. Create an empty stack and an empty output string.
2. Reverse the infix expression: Reverse the order of all elements in the infix expression, including
operands and operators.
3. Iterate through the reversed infix expression from left to right.
4. If the current character is an operand (number or variable), append it to the output string.
5. If the current character is a closing bracket ‘)’, push it onto the stack.
6. If the current character is an operator compare its precedence with the precedence of the
operator at the top of the stack.
7. If the current operator/incoming operator has higher precedence than the operator at the top of
the stack or the stack is empty, push the current operator onto the stack.
8. If the current operator has equal precedence than the operator at the top of the stack, Then push
the current operator onto the stack.
9. If the current operator has lower precedence than the operator at the top of the stack, pop the
operators from the stack and append them to the output string until an operator with
lower/equal precedence is encountered or the stack becomes empty. Then push the current
operator onto the stack.
10. If the current character is an opening bracket ‘(‘, pop the operators from the stack and append
them to the output string until the corresponding closing bracket ‘)’ is encountered. Discard the
closing bracket.
11. Repeat steps 4 to 10 until all characters in the reversed infix expression have been processed.
12. Pop the remaining operators from the stack and append them to the output string.
13. Reverse the output string to obtain the prefix expression.
Function
infix = InfixtoPrefix( stack, infix)
reverse(infix)
loop
if i = 0 istooperand
infix[i] infix.length
→ prefix+= infix[
i]
else if infix[i] is '(' → stack.push(infix
[i])
else if infix[i] is ')' → pop and print th
eotvalues
found of stack till the symbol ')' is n
else
^) → if infix[i] is an operator(+, -, *, /,
if
on the
the stack
top ofisthe
empty then push infix[i]
stack.
Else →
If precedence(infix[i] > precedence(sta
ck.top))
→
k Push infix[i] on the top of the stac
else
p) && if(infix[i]
infix[i] ==
== precedence(stack.to
'^')
→stackPop tilland
the print the top
condition is values of the
true
→
else Push infix[i] into the stack
p)) if(infix[i] == precedence(stack.to
→ Push infix[i] on to the stack
Else if(infix[i] < precedence(stack.top))
→ Pop the stack values and print them till the stack is not empty and infix[i] < precedence(stack.to
p)
→ Push infix[i] on to the stack
End loop
Pop and print the remaining elements of the stack
Prefix = reverse(prefix)
return
• Conversion of Infix to Prefix using Stack:
• Example: Infix expression: K + L - M * N + (O^P) * W/U/V * T + Q
• If we are converting the expression from infix to prefix, we need first to
reverse the expression.
• The Reverse expression would be: Q + T * V/U/W * ) P^O(+ N*M - L + K
• To obtain the prefix expression, we have created a table that consists of three
columns, i.e., input expression, stack, and prefix expression. When we
encounter any symbol, we simply add it into the prefix expression. If we
encounter the operator, we will push it into the stack.
• Example1: (A+B^C)*D+E^5
• Example2: (A+B)*C-D+F
Input expression Stack Prefix expression
Q Q
+ + Q
T + QT
* +* QT
V +* QTV
/ +*/ QTV
U +*/ QTVU
/ +*// QTVU
W +*// QTVUW
* +*//* QTVUW
) +*//*) QTVUW
P +*//*) QTVUWP
^ +*//*)^ QTVUWP
O +*//*)^ QTVUWPO
( +*//* QTVUWPO^
+ ++ QTVUWPO^*//*
N ++ QTVUWPO^*//*N
* ++* QTVUWPO^*//*N
M ++* QTVUWPO^*//*NM
- ++- QTVUWPO^*//*NM*
L ++- QTVUWPO^*//*NM*L
+ ++-+ QTVUWPO^*//*NM*L
K ++-+ QTVUWPO^*//*NM*LK
QTVUWPO^*//*NM*LK+-++
• The above expression, i.e., QTVUWPO^*//*NM*LK+-++, is not a final
expression. We need to reverse this expression to obtain the prefix expression.
• Final prefix expression is: ++-+KL*MN*//*^OPWUVTQ
* Pop -CD from the stack. *+AB - CD Pop two operands from the stack, i.e.,
Pop +AB from the stack. -CD and +AB. Add '*' operator before
Push *+AB -CD into the stack. +AB then the expression would
become *+AB-CD.
QUEUE
• Queue is a data structure that is similar to the queue in the real world.
• A queue is a linear data structure in which whatever comes first will go out
first, and it follows the FIFO (First-In-First-Out) policy.
• Queue can also be defined as the list or collection in which the insertion is
done from one end known as the rear end or the tail of the queue, whereas
the deletion is done from another end known as the front end or the head of
the queue.
• The real-world example of a queue is the ticket queue outside a cinema hall,
where the person who enters first in the queue gets the ticket first, and the last
person enters in the queue gets the ticket at last.
• Similar approach is followed in the queue in data structure.
• The representation of the queue is shown in the below image
Types of Queue: There are four different types of queue that are listed as
follows
1. Simple Queue or Linear Queue
2. Circular Queue
3. Priority Queue
4. Double Ended Queue( or Deque)
• This difficulty can overcome if we treat queue position with index 0 as a position that comes
after position with index 4 i.e., we treat the queue as a circular queue.
C program for Queue using Arrays
#include<stdio.h> #include<stdlib.h> switch(choice) {
#define maxsize 5 case 1:
void enqueue(); enqueue();
void dequeue(); break;
void display(); case 2:
int front = -1, rear = -1; dequeue();
int queue[maxsize]; break;
void main () { case 3:
int choice; display();
while(choice != 4) { break;
printf("\n*****Main Menu*****\n"); case 4:
printf("\n1.insert an element\n2.Delete an exit(0);
element\n3.Display the queue\n4.Exit\n"); break;
printf("\nEnter your choice:"); default: printf("\nEnter valid choice??\n");
scanf("%d",&choice); } } }
void enqueue() { void dequeue() {
int item; int item;
printf("\nEnter the element:"); if (front == -1 || front > rear)
scanf("\n%d",&item); {
if(rear == maxsize-1) printf("\n QUEUE IS UNDERFLOW\n");
{ }
printf("\n QUEUE IS OVERFLOW\n"); else
{
}
item = queue[front];
if(front == -1 && rear == -1)
printf("\ndeleted value=%d",item);
{
if(front == rear)
front = rear = 0;
{
}
front = rear= -1 ;
queue[rear] = item;
}
printf("\nValue inserted ");
else {
rear = rear+1;
front = front + 1;
}
}}}
void display() {
int i;
if(rear == -1)
{
printf("\nEmpty queue\n");
}
else
{
printf("\n Queue elements are:");
for(i=front;i<=rear;i++)
{
printf("%d\t\t",queue[i]);
} } }
• Simple Queue or Linear Queue using Linked List Representation:
Operations:
Step 1 - Include all the header files which are used in the program. And declare all the user defined
functions.
Step 2 - Define a 'Node' structure with two members data and next.
Step 3 - Define two Node pointers 'front' and 'rear' and set both to NULL.
Step 4 - Implement the main method by displaying Menu of list of operations and make suitable function
calls in the main method to perform user selected operation.
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 given value and set 'newNode → next' to NULL.
Step 2 - Check whether queue is Empty (rear == NULL)
Step 3 - If it is Empty then, set front = newNode and rear = newNode.
Step 4 - If it is Not Empty then, set rear → next = newNode and rear = newNode.
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 == 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 = front → next' and delete 'temp' (free(temp)).
display() - Displaying the elements of Queue: We can use the following steps to display the elements
(nodes) of a queue...
Step 1 - Check whether queue is Empty (front == NULL).
Step 2 - If it is Empty then, display 'Queue is Empty!!!' and terminate the function.
Step 3 - If it is Not Empty then, define a Node pointer 'temp' and initialize with front.
Step 4 - Display 'temp → data --->' and move it to the next node. Repeat the same until 'temp' reaches to
'rear' (temp → next != NULL).
Step 5 - Finally! Display 'temp → data ---> NULL'.
struct Node{ void main(){
int choice, value;
int data; printf("\n:: Queue Implementation using Linked List ::\n");
while(1!=5){
struct Node *next; printf("1. Insert\n2. Delete\n3. Display\n4. Exit\n");
printf("Enter your choice: "); scanf("%d",&choice);
}*front = NULL,*rear = switch(choice){
NULL; case 1: printf("Enter the value to be insert: "); scanf("%d",
&value);
void insert(int); insert(value); break;
case 2: delete(); break;
void delete(); case 3: display(); break;
case 4: exit(0);
void display(); default: printf("\nWrong selection!!! Please try again!!!\
n");
} }}
void insert(int value){ void delete(){
struct Node *newNode;
newNode = (struct Node*)malloc(sizeof(struct if(front == NULL)
Node));
newNode->data = value;
printf("\nQueue is Empty!!!\n");
newNode -> next = NULL; else{
if(front == NULL)
{
struct Node *temp = front;
front = rear = newNode; front = front -> next; printf("\
front->next=NULL; nDeleted element: %d\n",
rear->next=NULL;
} temp->data);
else{ free(temp);
rear -> next = newNode;
rear = newNode; }}
}
printf("\nInsertion is Success!!!\n");
}
void display(){
if(front == NULL)
printf("\nQueue is Empty!!!\n");
else{
struct Node *temp = front;
while(temp->next != NULL)
{
printf("%d--->",temp->data);
temp = temp -> next;
}
printf("%d--->NULL\n",temp->data);
}}
• Representation of 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. It is also known as a Ring Buffer.
• Operations on Circular Queue: The following are the operations that can be performed on a circular
queue:
Front: It is used to get the front element from the Circular Queue.
Rear: It is used to get the rear element from the Circular Queue.
enQueue(value): This function is used to insert the new value in the Circular Queue. The new
element is always inserted from the rear end.
deQueue(): This function deletes an element from the Queue. The deletion in a Circular Queue
always takes place from the front end.
• Scenarios for inserting an element
•There are two scenarios in which queue is not full:
If rear != max - 1, then rear will be incremented to mod(maxsize) and the new value will be
inserted at the rear end of the queue.
If front != 0 and rear = max - 1, it means that queue is not full, then set the value of rear to 0
and insert the new element there.
•There are two cases in which the element cannot be inserted:
When front ==0 && rear = max-1, which means that front is at the first position of the Queue
and rear is at the last position of the Queue.
front== rear + 1;
• Let us consider a circular queue, which can hold
maximum (MAX) of six elements. Initially the
queue is empty (i.e ., front=-1 and rear=-1) then set
front=0 and rear=0.