0% found this document useful (0 votes)
16 views19 pages

Stacks and Queues: ADT and Applications

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)
16 views19 pages

Stacks and Queues: ADT and Applications

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

UNIT – II

Stacks and Queues


The Stack ADT: Stack Model, Array implementation of stacks, Applications: Balancing the
symbols, Infix to Postfix Conversion, Postfix Evaluation.
The Queue ADT: Queue Model, Array implementation of Queues, Applications of Queues,
Circular Queues

THE STACK ADT: Stack Model


Definition: A stack is a linear data structure that follows the Last-In-First-Out (LIFO) order. In
a stack, all operations are performed at one end called the top.
A typical example can be a stack (or a bundle) of books. The books can be arranged one on the
other, when we add a book, it is always placed on the previous book and while removing the
book, the recently placed book can be removed.

The following Figure illustrates this sequence of operations.

Array Implementation Of Stacks


Stacks can be represented as a linear array. Every stack has a variable called TOP associated
with it, which is used to store the address of the topmost element of the stack. If TOP == NULL,
then it indicates that the stack is empty and if TOP == MAX–1, then the stack is full.

Operations On a Stack
The following operations can be performed on a stack:
push, pop, top, display, isfull, isempty.

Push Operation:
When a stack is implemented using an array, the push operation involves the following steps:
1. Check for Overflow: Ensure that there is enough space to add a new element. In fixed-
size stacks, this means checking if the stack is already full.
2. Increment the Top Index: Move the top index to the next position to make space for the
new element.
3. Add the New Element: Place the new element at the position indicated by the top index.
Step-by-step algorithm for performing the push operation on a stack implemented using an
array:
1. Start
2. Check for Stack Overflow
o if top >= maxSize - 1:
print "Stack Overflow"
return
3. Increment the Top Index
o top = top + 1
4. Add the New Element
o stack[top] = new Element
5. End

Pop Operation
When a stack is implemented using an array, the pop operation involves the following steps:
1. Check for Underflow: Ensure that the stack is not empty. If it is, the operation cannot
proceed.
2. Return the Top Element: Retrieve the element located at the current top index.
3. Decrement the Top Index: Move the top index down to the previous element.
These steps ensure that the stack maintains its LIFO order and can efficiently handle
the removal of elements.

Step-by-step algorithm for performing the pop operation on a stack implemented using an
array:
1. Start
2. Check for Stack Underflow
o if top == -1:
print "Stack Underflow"
return None
3. Return the Top Element
o element = stack[top]
4. Decrement the Top Index
o top = top - 1
5. End

Top Operation
 Top is an operation that returns the value of the topmost element of the stack without
deleting it from the stack.
 The top operation first checks if the stack is empty or not.

Stack Overflow: Trying to perform push operation on a stack that is already full is called as
stack overflow.
Stack Underflow: Trying to perform pop operation on a stack that is already empty is called
as stack underflow.

Program: Stack operations using arrays.


//stack using arrays
#include<stdio.h>
int stack[5],top=-1;
int max=5;
void push(int x)
{
if(top==max-1)
printf("stack over low");
else
{
top++;
stack[top]=x;
}
}
void pop()
{
if(top==-1)
printf("stack under low");
else
{
printf("%d is deleted",stack[top]);
top--;
}
}
void top_stack()
{
if(top==-1)
printf("stack is empty");
else
printf("%d is at top",stack[top]);
}
void display()
{
if(top==-1)
printf("stack is empty");
else
{
for(int i=top;i>=0;i--)
printf("%d ",stack[i]);
}
}
int main()
{
while(1)
{
int choice,x;
printf("\nenter your choice\n");
printf("[Link] [Link] [Link] [Link] [Link]");
scanf("%d",&choice);
if(choice==1)
{
printf("enter value to push");
scanf("%d",&x);
push(x);
}
else if(choice==2)
pop();
else if(choice==3)
top_stack();
else if(choice==4)
display();
else if(choice==5)
break;
else
printf("please enter valid choice");
}
return 1;
}
Output:
Applications Of Stacks:
Balancing The Symbols
Stacks can be used to check the validity of parentheses in any algebraic expression. An
algebraic expression is valid if for every open bracket there is a corresponding closing bracket.
The expression (A+B} is invalid but an expression {A + (B – C)} is valid.

Valid or balanced parentheses:


 [[{{(())}}]]
 [][][](){}

Invalid or imbalanced parentheses:


 ([)]
 ((()]))
 [{()]

Algorithm:
 Declare an array for character stack.
 Now traverse the expression string exp.
1. If the current character is an opening bracket either ( or{ or [, push it onto the
stack.
2. If the current character is a closing bracket either ) or }or ], then:
 If the stack is empty, then the given expression is not balanced.
 If a matching opening bracket is present at the top of the stack, then
apply pop operation. Else, the given expression is not balanced.
3. If stack is empty after processing all the elements, then the string is balanced.

Program
//Balancing paranthesis, application of stack
#include<stdio.h>
#include<string.h>
char stack[10];
int top=-1;
int max=10;
void push(char x)
{
if(top==max-1)
printf("stack overflow");
else
{
top++;
stack[top]=x;
}
}

void pop()
{
if(top==-1)
printf("stack underflow");
else
{
//printf("%c is deleted",stack[top]);
top--;
}
}

bool isEmpty()
{
if(top==-1)
return 1;
else
return 0;
}

int main()
{
char expr[] = "()[{]";
printf("%s\n",expr);
int len = strlen(expr);
for(int i=0;i<len;i++)
{
if(expr[i]=='(' || expr[i]=='[' || expr[i]=='{')
{
push(expr[i]);
}
else if(expr[i]==')' || expr[i]==']' || expr[i]=='}')
{
if(isEmpty())
{
printf("not balanced\n");
return 1;
}
else if((expr[i]==')' && stack[top]=='(') || (expr[i]==']' &&
stack[top]=='[') || (expr[i]=='}' && stack[top]=='{'))
{
pop();
}
else
{
printf("not balanced\n");
return 1;
}
}
}
if(isEmpty())
printf("balanced\n");
else
printf("not balanced\n");
return 1;
}

Output:
()[{]
not balanced

Evaluation of Arithmetic Expressions: Polish Notations


Infix, postfix, and prefix notations are three different but equivalent notations of writing
algebraic expressions.

Infix Expressions
Infix expressions are mathematical expressions where the operator is placed between its
operands. This is the most common mathematical notation used by humans.
Example: the expression “2 + 3” is an infix expression, where the operator “+” is placed
between the operands “2” and “3”.
Advantages of Infix Expressions
 More natural and easier to read and understand.
 Widely used and supported by most programming languages.
Disadvantages Infix Expressions
 Requires parentheses to specify the order of operations.
 Can be difficult to parse and evaluate efficiently.

Prefix Expressions (Polish Notation)


Prefix expressions are also known as Polish notation, are a mathematical notation where the
operator precedes its operands.
In prefix notation, the operator is written first, followed by its operands. For example, the
infix expression “a + b” would be written as “+ a b” in prefix notation.
Advantages of Prefix Expressions
 No need for parentheses, as the operator always precedes its operands.
 Easier to parse and evaluate using a stack-based algorithm.
Disadvantages of Prefix Expressions
 Can be difficult to read and understand for humans.
 Not as commonly used as infix notation.

Postfix Expressions (Reverse Polish Notation)


Postfix expressions are also known as Reverse Polish Notation (RPN), are a mathematical
notation where the operator follows its operands.
In postfix notation, operands are written first, followed by the operator. For example, the
infix expression “5 + 2” would be written as “5 2 +” in postfix notation.
Advantages of Postfix Notation
 Also eliminates the need for parentheses.
 More commonly used than prefix notation.
Disadvantages of Postfix Expressions
 Requires a stack-based algorithm for evaluation.

Conversion of an Infix Expression into a Postfix Expression


Below are the steps to convert infix expression to postfix expression:
1. Scan the infix expression from left to right.
2. If the scanned character is an operand, put it in the postfix expression.
3. If the scanned character is a ‘(‘, push it to the stack.
4. Otherwise, do the following
 If the precedence of the current scanned operator is higher than the precedence of the
operator on top of the stack, or if the stack is empty, or if the stack contains a ‘(‘, then
push the current operator onto the stack.
 Else, pop all operators from the stack that have precedence higher than or equal to that
of the current operator, and append the popped operators into output. Push the current
operator into 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.
Program:
//infix to postfix, application of stack.
#include <stdio.h>
#include <ctype.h>
#include <string.h>

// Function to return precedence of operators


int prec(char c) {

if (c == '^')
return 3;
else if (c == '/' || c == '*')
return 2;
else if (c == '+' || c == '-')
return 1;
else
return -1;
}

int main() {
char exp[] = "(a+b)*(c+d)";
printf("given infix expression: %s\n",exp);
int len = strlen(exp);
char result[len+1];
char stack[len];
int j = 0;
int top = -1;

for (int i = 0; i < len; i++) {


char c = exp[i];

// If the scanned character is an operand, add it to the output string.


if (isalnum(c))
result[j++] = c;

// If the scanned character is ‘(‘, push it to the stack.


else if (c == '(')
stack[++top] = '(';

// If the scanned character is an ‘)’, and add to the output string from the stack
// until an ‘(‘ is encountered.
else if (c == ')') {
while (top != -1 && stack[top] != '(') {
result[j++] = stack[top--];
}
top--;
}
// If an operator is scanned
else {
while (top != -1 && (prec(c) <= prec(stack[top]))){
result[j++] = stack[top--];
}
stack[++top] = c;
}
}

// Pop all the remaining elements from the stack


while (top != -1) {
result[j++] = stack[top--];
}

result[j] = '\0';
printf("postfix expression is %s\n", result);
return 0;
}

Output:
given infix expression: (a+b)*(c+d)
postfix expression is ab+cd+*

Evaluation of a Postfix Expression


 Every character of the postfix expression is scanned from left to right.
 If the character encountered is an operand, it is pushed on to the stack.
 However, if an operator is encountered, then the top two values are popped from the
stack and the operator is applied on these values.
 The result is then pushed on to the stack.

E.g.: Consider the infix expression given as 9 – ((3 * 4) + 8) / 4. Evaluate the expression.
The infix expression 9 – ((3 * 4) + 8) / 4 can be written as 9 3 4 * 8 + 4 / – using postfix
notation.
Program:
//Postfix evaluation, application of stack
#include<stdio.h>
#include<string.h>
#include<ctype.h>
int main()
{
char exp[]="462*-31-62/++";
printf("postfix expression is %s\n",exp);
int len=strlen(exp);
char stack[len];
int num, top=-1, n1, n2, res;
for(int i=0;i<len;i++)
{
char c = exp[i];
if(isalnum(c))
{
num = c-48;
stack[++top] = num;
}
else
{
n1 = stack[top--];
n2 = stack[top--];
switch(c)
{
case '+':
res = n2+n1;
break;
case '-':
res = n2-n1;
break;
case '*':
res = n2*n1;
break;
case '/':
res = n2/n1;
break;
}
stack[++top]=res;
}
}
printf("result is %d\n", stack[top]);
return 0;
}
Output:
postfix expression is 462*-31-62/++
result is -3
THE QUEUE ADT: Queue Model
Definition: A queue is a linear data structure that follows the First-In-First-Out (FIFO) order.
In a stack. The elements in a queue are added at one end called the REAR and removed from
the other end called the FRONT. Queues can be implemented by using either arrays or linked
lists.
Example: In operating systems, jobs (like print jobs or process execution) are added to a queue.
The operating system executes the jobs in the order they were received.

Queue operations:
 enqueue(): inserts element at the rear end of the queue.
 dequeue(): Removes element from the front end of the queue.
 Front: Returns the element at the front end of the queue.
 Rear: Returns the element at the rear end of the queue.
 isEmpty(): To check if the queue is empty
 isFull(): To check whether the queue is full or not

Enqueue is the operation used to insert an element into the queue. It adds the new element at
the rear of the queue, ensuring that the FIFO order is maintained.
When performing an enqueue operation, the following steps are executed:
1. Check for Overflow: Verify if the queue is full. In a fixed-size queue, if the rear index
has reached the maximum size, an overflow condition occurs, and the element cannot
be added.
2. Increment Rear: Move the rear index to the next position to point to the location where
the new element will be inserted. Increment front only for first insertion.
3. Insert Element: Place the new element at the position indicated by the rear index.

Enqueue algorithm:
Start
if rear is size - 1:
print "Queue Overflow"
else:
if front is -1
increment front
increment rear
queue[rear] = element
End

Dequeue is the operation used to remove an element from the queue. This operation ensures
that the FIFO order is maintained by always removing the element from the front of the queue.
When performing a dequeue operation, the following steps are executed:
1. Check for Underflow: Verify if the queue is empty. If the front index has surpassed the
rear index or if front is -1, an underflow condition occurs, and there are no elements to
remove.
2. Remove Element: Retrieve and remove the element at the front index.
3. Increment Front: Move the front index to the next position to point to the new front of
the queue.

Dequeue algorithm:
Start
if front > rear or front is -1:
print "Queue Underflow"
else:
element = queue[front]
increment front
return element
End

Program: Queue operations using arrays.


//Queue using arrays
#include<stdio.h>
int queue[5];
int max=5,front=-1,rear=-1;
void enqueue()
{
int item;
if(rear==max-1)
printf("Queue Overflow\n");
else
{
if(front==-1)
front=0;
printf("enter element to insert: ");
scanf("%d",&item);
queue[++rear]=item;
}
}

void dequeue()
{
if(front==-1 || front>rear)
printf("queue underflow\n");
else
printf("%d deleted\n",queue[front++]);
}

void show()
{
if(front==-1 || front>rear)
printf("queue empty\n");
else
{
for(int i=front;i<=rear;i++)
printf("%d ",queue[i]);
}
}

void qfront()
{
if(front==-1 || front>rear)
printf("queue empty\n");
else
printf("%d is at front\n",queue[front]);
}

void qrear()
{
if(front==-1 || front>rear)
printf("queue empty\n");
else
printf("%d is at rear\n",queue[rear]);
}

int main()
{
while(1)
{
int ch;
printf("chooce your operation \n");
printf("[Link] [Link] [Link] [Link] [Link] [Link]\n");
scanf("%d",&ch);
switch(ch)
{
case 1:
enqueue();
break;
case 2:
dequeue();
break;
case 3:
show();
break;
case 4:
qfront();
break;
case 5:
qrear();
break;
case 6:
return 1;
default:
printf("please enter valid choice");
}
}
}
Output:

Disadvantage of Linear queue: After performing certain number of insertions and deletions
on a linear queue, we may not be able to insert new items into the queue even if the queue has
empty locations.
For Example: Consider the following queue:

30 40 50
0 1 2 3 4
front rear
Here we cannot insert any more elements into the queue even if there are no items at indices 0
and 1. This is because the queue full condition rear == max-1 becomes true. Hence a linear
queue can result in wastage of memory. This can be prevented using circular queues.

Circular Queue
A Circular Queue is a type of queue where the last element of the queue is connected to the
first element of the queue forming a circle.
In a circular queue, rear is updated using rear = (rear+1)%size. This makes circular iterations
possible by moving to index 0 after the last index of the queue.
Similarly front is also updated using front= (front+1)%size.
Enqueue algorithm in Circular queue:
Start
if front is (rear+1)%size:
print "Queue Overflow"
else:
if front is -1
increment front
rear = (rear+1)%size
queue[rear] = element
End

Dequeue algorithm in Circular queue:


Start
if front is -1:
print "Queue Underflow"
else:
element = queue[front]
if front==rear
front=-1
rear=-1
else
front= (front+1)%size
return element
End

Program: Circular queue using arrays.


//Circular queue using arrays
#include<stdio.h>
int queue[5];
int size=5,front=-1,rear=-1;
int item;
void enqueue()
{
int item;
if(front==(rear+1)%size)
printf("queue overflow\n");
else
{
if(front==-1)
front=0;
printf("enter value to insert");
scanf("%d",&item);
rear=(rear+1)%size;
queue[rear]=item;
}
}

void dequeue()
{
if(front==-1)
printf("queue underflow\n");
else
{
item = queue[front];
if(front==rear)
{
front=-1;
rear=-1;
}
else
{
front=(front+1)%size;
}
printf("%d is deleted\n",item);
}
}

void show()
{
if(front==-1)
printf("queue empty");
else
{
for(int i=front;i!=rear;i=(i+1)%size)
{
printf("%d ",queue[i]);
}
printf("%d",queue[rear]);
}
}

void qfront()
{
if(front==-1)
printf("queue empty\n");
else
printf("%d is at front\n",queue[front]);
}

void qrear()
{
if(front==-1)
printf("queue empty\n");
else
printf("%d is at rear\n",queue[rear]);
}

int main()
{
while(1)
{
int ch;
printf("enter your choice\n");
printf("[Link] [Link] [Link] [Link] [Link], [Link]");
scanf("%d",&ch);
switch(ch)
{
case 1:
enqueue();
break;
case 2:
dequeue();
break;
case 3:
show();
break;
case 4:
qfront();
break;
case 5:
qrear();
break;
case 6:
return 1;
default:
printf("please enter valid option\n");
}
}
return 1;
}
Output:

Applications of Queues:
1. Operating System Scheduling: In operating systems, CPU scheduling and task
scheduling are based on queues, where processes are executed in the order, they are ready
to run. Round-robin scheduling in operating systems is often implemented using a circular
queue.
2. Traffic Management Systems: In traffic control and toll booths, vehicles often form a
queue as they wait for their turn. This is an example of queue real-time applications where
vehicles are processed in the order they arrive.
3. Print Spooling: When multiple print jobs are submitted to a printer, they are stored in a
queue until processed one by one. The print spooler is an example of a queue application
in real life.
4. Data Buffers in Networking: In networking, data packets arriving at a router are stored
in a queue before being transmitted to the destination. This helps in managing traffic flow
and prevents packet loss. Network traffic management heavily relies on queues.
5. Queue in Customer Service Centres: Queues are most commonly observed in customer
service systems like banking, airports, and customer helplines. Customers are served in the
order they arrive, ensuring fairness. This is a straightforward implementation of a simple
queue.

You might also like