Professional Documents
Culture Documents
Stacks & Queues Material
Stacks & Queues Material
i.e.
A stack is a data structure in which addition of new element or deletion of existing element
always takes place at the same end. This end is often known as top of stack.
Example:
This situation can be compared to a stack of plates in a cafeteria where every new plate
added to the stack is added at the top. similarly, every new plate taken off the stack is also from the
top of the stack.
The two changes that can be made to a stack are given special names:
When an item is removed from the stack the operation is called pop.
If the elements are added continuously to the stack it grows at the one end.
The abstract data type for stack are push(), pop() and top() they are implemented in two ways.
1. Using an arrays
Stack as an array:-
An array is used to store the ordered list of elements. Hence it is very easy to manage a
stack if we represent it using an array, however the problem with an array is that we are
required to declare the size of the array before using in it a program, i.e. size of array should
be fixed.
Though an array and stack are totally different data structures, an array can be used to
store the elements of a stack. We declare the array with a maximum size large enough to
manage a stack. As a result the stack can grow or shrink with in the space reserved for it.
Source code:-
#include<stdio.h>
#include<conio.h>
#include<alloc.h>
#define MAX 10
struct stack
int arr[MAX];
int top;
};
void main()
struct stack s;
int i;
clrscr();
s.top=-1;
push(&s,11);
push(&s,23);
push(&s,8);
push(&s,16);
push(&s,27);
push(&s,14);
push(&s,20);
push(&s,39);
push(&s,2);
push(&s,15);
for(i=0;i<MAX;i++)
printf("%d\t",s.arr[i]);
push(&s,7);
pop(&s);
pop(&s);
pop(&s);
pop(&s);
pop(&s);
for(i=0;i<=s.top;i++)
printf("%d\t",s.arr[i]);
getch();
}
//adds an element to the stack
if(s->top==MAX-1)
printf("\nstack is full");
return;
s->top++;
s->arr[s->top]=item;
int data,i;
if(s->top==-1)
printf("\nstack is empty");
return ;
data=s->arr[s->top];
s->arr[s->top]=NULL;
printf("\nitem popped: %d",data);
s->top--;
In the earlier we had used arrays to store the elements that get added to the stack,
however, when implemented as an array it suffers from the basic limitation of an array i.e
size cannot be increased or decreased once it is declared.
This problem can be overcome if we implement a stack using a linked list. In case of a
linked stack we shall push and pop nodes from one end of a linked list. The stack as linked
list is represented as a singly connected list.
Struct node
};
#include<stdio.h>
#include<conio.h>
#include<alloc.h>
struct node
int data;
};
void main()
int i;
clrscr();
push(&s,14);
push(&s,3);
push(&s,18);
push(&s,29);
push(&s,31);
push(&s,16);
display(s);
i=pop(&s);
i=pop(&s);
i=pop(&s);
display(s);
getch();
temp=malloc(sizeof(struct node));
temp->data=item;
temp->link=*top;
*top=temp;
int item;
if(*top==NULL)
return NULL;
temp=*top;
item=temp->data;
*top=(*top)->link;
free(temp);
return item;
}
void display(struct node *s)
int c=0;
printf("\n");
while(s!=NULL)
printf("%d\t",s->data);
c++;
s=s->link;
Applications of stack:-
Parsing
Calling function
Recursive function
Conversion of arithmetic expression
Evaluation arithmetic expression
Polish notation:-
The place where stacks are frequently used is in evaluation of arithmetic expression. An
arithmetic expression consists of operands and operators. The operands can be numeric values or
numeric variables. The operators used in an arithmetic expression represent the operations like
addition, subtraction, multiplication, division and exponentiation.
When higher level programming languages came in to existence one of the major hurdles
faced by the computer scientists was to generate machine language instructions that would properly
evaluate any arithmetic expression.
To fix the order of evaluation of an expression each language assigns to each operator a
priority. Even after assigning priorities the computer failed to generate machine language instruction
that would properly evaluate the arithmetic expression like (A+B)*C.
“Then a polish mathematician Jan Lukasiewicz suggested a notation called Polish notation,
which gives two alternatives to represent an arithmetic expression.”
The two alternative notations are “prefix” and “postfix”. The fundamental property of
polish notation is that the order in which the operations are to be performed is completely
determined by the positions of the operators and operands in expression. Hence parentheses are
not required while writing expressions in polish notation.
Conversion of infix to postfix notation:- To convert the infix expression in to a post fix expression,
follow the below steps depending on the type of character scanned in infix.
Algorithm:-
f. If the character scanned happens to be an closing parenthesis, then pop the operators
present in the stack until encounter a open parentheses. Add the popped operators to the
output.
g. After completing the scanning of elements in infix expression, pop the all elements from the
stack one by one and add those popped elements to output.
Example :
((a+b)^c-(d*e)/f)
STACK OUTPUT
(( A
((+ AB
( AB+
(^ AB+
(^ AB+C
(- AB+C^
(-( AB+C^
(-( AB+C^D
(-(* AB+C^D
(-(* AB+C^DE
(- AB+C^DE*
(-/ AB+C^DE*F
AB+C^DE*F/-
Source code:-
#include<stdio.h>
#include<conio.h>
#include<string.h>
#include<ctype.h>
#define MAX 50
struct infix
{
char target[MAX];
char stack[MAX];
char *s,*t;
int top;
};
void initinfix(struct infix *);
void setexpr(struct infix *, char *);
void push(struct infix *, char);
char pop(struct infix *);
void convert(struct infix *);
int priority(char);
void show(struct infix);
void main()
{
struct infix p;
char expr[MAX];
clrscr();
initinfix(&p);
printf("Enter an expression in infix form:\n");
gets(expr);
setexpr(&p,expr);
convert(&p);
printf("The postfix expression of given infix form:\n");
show(p);
getch();
}
//initializes structure elements
void initinfix(struct infix *p)
{
p->top=-1;
strcpy(p->target,"");
strcpy(p->stack,"");
p->t=p->target;
p->s="";
}
//sets s to point to given expr
void setexpr(struct infix *p,char *str)
{
p->s=str;
}
//adds an operator to the stack
void push(struct infix *p,char c)
{
if(p->top==MAX)
printf("stack is full\n");
else
{
p->top++;
p->stack[p->top]=c;
}
}
//pops an operator from the stack
char pop(struct infix *p)
{
if(p->top==-1)
{
printf("stack is empty\n");
return -1;
}
else
{
char item=p->stack[p->top];
p->top--;
return item;
}
}
//converts the given expr from infix to postfix form
void convert(struct infix *p)
{
char opr;
while(*(p->s))
{
if(*(p->s)==' '||*(p->s)=='\t')
{
p->s++;
continue;
}
while(isdigit(*(p->s))||isalpha(*(p->s)))
{
*(p->t)=*(p->s);
p->s++;
p->t++;
}
if(*(p->s)=='(')
{
push(p,*(p->s));
p->s++;
}
if(*(p->s)=='*'||*(p->s)=='+'||*(p->s)=='/'||*(p->s)=='%'||*(p->s)=='-'||*(p->s)=='$')
{
if(p->top!=-1)
{
opr=pop(p);
while(priority(opr)>=priority(*(p->s)))
{
*(p->t)=opr;
p->t++;
opr=pop(p);
}
push(p,opr);
push(p,*(p->s));
}
else
push(p,*(p->s));
p->s++;
}
if(*(p->s)==')')
{
opr=pop(p);
while((opr)!='(')
{
*(p->t)=opr;
p->t++;
opr=pop(p);
}
p->s++;
}
}
while(p->top!=-1)
{
char opr=pop(p);
*(p->t)=opr;
p->t++;
}
*(p->t)='\0';
}
//returns the pripority of an operator
int priority(char c)
{
if(c=='$')
return 3;
if(c=='*'||c=='%'||c=='/')
return 2;
else
{
if(c=='+'||c=='-')
return 1;
else
return 0;
}
}
// display the postfix form of the given expr
void show(struct infix p)
{
printf("%s",p.target);
}
a. Write a post fix expression in a string format and scan the expression character by
character.
b. If the character scanned is an operand, push that in to a stack.
c. If the character scanned is an operator, pop the two topmost elements from the stack.
Then perform the operation performed by the scanned operator between the two
popped elements from stack. Now push the result in to a stack.
d. Repeat the steps b and c for remaining all characters in postfix expression.
e. After completion of scanning the characters, pop the topmost value from the stack and
mark as final result.
Example:-
Source code:-
#include<stdio.h>
#include<conio.h>
#include<stdlib.h>
#include<math.h>
#include<ctype.h>
#include<string.h>
#define MAX 50
struct postfix
{
int stack[MAX];
int top,nn;
char *s;
};
void initpostfix(struct postfix *);
void setexpr(struct postfix *,char *);
void push(struct postfix *,int);
int pop(struct postfix *);
void calculate(struct postfix *);
void show(struct postfix);
void main()
{
struct postfix q;
char expr[MAX];
clrscr();
initpostfix(&q);
printf("Enter postfix expression to evaluate:\t");
gets(expr);
setexpr(&q,expr);
calculate(&q);
show(q);
getch();
}
//initializes structure elements
void initpostfix(struct postfix *p)
{
p->top=-1;
}
//set s to point to given expression
void setexpr(struct postfix *p,char *str)
{
p->s=str;
}
//adds digit to the stack
void push(struct postfix *p,int item)
{
if(p->top==MAX-1)
printf("stack is full \n");
else
{
p->top++;
p->stack[p->top]=item;
}
}
//pops digit from the stack
int pop(struct postfix *p)
{
int data;
if(p->top==-1)
{
printf("stack is empty\n");
return NULL;
}
data=p->stack[p->top];
p->top--;
return data;
}
// evaluate the postfix expression
void calculate(struct postfix *p)
{
int n1,n2,n3;
while(*(p->s))
{
//skip whitespace. if any
if(*(p->s)==' '||*(p->s)=='\t')
{
p->s++;
continue;
}
//if digit is encountered
if(isdigit(*(p->s)))
{
p->nn=*(p->s)-'0';
push(p,p->nn);
}
else
{
//if operator is encountered
n1=pop(p);
n2=pop(p);
switch(*(p->s))
{
case'+':
n3=n2+n1;
break;
case'-':
n3=n2-n1;
break;
case'/':
n3=n2/n1;
break;
case'*':
n3=n2*n1;
break;
case'%':
n3=n2%n1;
break;
case'$':
n3=(int)pow(n2,n1);
break;
default:
printf("unknown operator\n");
exit(1);
}
push(p,n3);
}
p->s++;
}
}
//displays the result
void show(struct postfix p)
{
p.nn=pop(&p);
printf("result is %d",p.nn);
}
QUEUES
Queue:- Queue is linear data structures in which insertion of new elements takes place at one
end called rear, and deletion of existing elements takes place from another end called front.
FIFO:- The queue is also called first in first out system because of first inserted element is
deleted first.
The two changes that can be made to a queue are given special names:
When an item is added at rear end to queue , the operation is called Enqueue
When an item is removed from front end of the queue, the operation is called
Dequeue.
An array is used to store the ordered list of elements. Hence it is very easy to manage
a queue if we represent it using arrays. However the problem with an array is that we are
required to declare the size of the array before using it in a program.
Though an array and queues are totally different data structures, an array can be used
to store the elements in a queue. We can declare the array with maximum size large enough
to manage queue.
Suppose if the user wants to insert on more item to the queue, do the following:
Suppose if the user wants to delete an existing element from the queue, do the
following:
One-dimensional array
A variable to indicate the front position
Another variable to indicate the rear position.
PROGRAM:-
#include<stdio.h>
#include<conio.h>
#define MAX 10
struct queue
int arr[MAX];
int front,rear;
};
void enqueue(struct queue *,int );
void main()
int k;
clrscr();
p->front=p->rear=-1;
enqueue(p,23);
enqueue(p,9);
enqueue(p,11);
enqueue(p,18);
enqueue(p,25);
enqueue(p,16);
enqueue(p,17);
enqueue(p,22);
enqueue(p,19);
enqueue(p,30);
for(k=p->front;k<MAX;k++)
printf("%d\t",p->arr[k]);
enqueue(p,30);
dequeue(p);
dequeue(p);
dequeue(p);
for(k=p->front;k<MAX;k++)
printf("%d\t",p->arr[k]);
getch();
if(l->rear==MAX-1)
printf("\nQueue is full");
return;
}
l->rear++;
l->arr[l->rear]=item;
if(l->front==-1)
l->front=0;
int data,k;
if(l->front==-1)
printf("\n Q is empty");
return;
data=l->arr[l->front];
l->arr[l->front]=NULL;
if(l->front==l->rear)
l->front=l->rear=-1;
else
l->front++;
}
Implementation of queues using linked lists:-
It is not possible to predict the size of the queue in advance and it may vary from time
to time. Hence array implementation may not be possible. To overcome this draw back linked
list implementation can be used.
Source code:-
#include<stdio.h>
#include<conio.h>
#include<alloc.h>
void main()
{
struct queue *front,*rear;
front=rear=NULL; //empty linked list
addq(&front,&rear,10);
addq(&front,&rear,17);
addq(&front,&rear,18);
addq(&front,&rear,5);
addq(&front,&rear,30);
addq(&front,&rear,15);
clrscr();
printf("before deletion:\n");
display(front);
delq(&front,&rear);
delq(&front,&rear);
delq(&front,&rear);
printf("\nafter deletion:\n");
display(front);
getch();
}
Advantages:-
No wastage of memory
Insertion and deletion operations are easy to do
Size of the queue is not fixed. Hence any number of elements can be place in a
queue dynamically.
Circular queue:-
The queue that we implemented using an array suffers from on limitation. In that
implementation there is a possibility that the queue is reported as full (since rear has
reached the end of the array), even though there might be empty slots at the beginning of
the queue.
This disadvantage can be overcome by using the circular array representation of the
queue.
n 2
N-1 3
Rear
. . . . 4
Front
. . . . 5
(Logical)
here as we go on adding elements to the queue and reach the end of the array,
the next element is stored in the first slot of the array(provided it is free).
Instead of reporting the queue as full, if some elements in the queue have
been deleted then there might be empty slots at the beginning of queue. In such case these
slots would be filled by a new elements being added to the queue. In short just because
we have reached the end of the array the queue would not be reported as full.
The queue would be reported as full only when all the slots in the array stand
occupied.
ii. Dequeue () : removes and returns an item from the front end of the queue
Source code:-
#include<stdio.h>
#include<conio.h>
#define MAX 10
struct cirq
int arr[MAX];
int front,rear;
};
int i;
clrscr();
p->front=p->rear=-1;
addq(p,23);
addq(p,9);
addq(p,11);
addq(p,10);
addq(p,25);
for(i=0;i<MAX;i++)
printf("%d\t",p->arr[i]);
delq(p);
delq(p);
delq(p);
printf("\nthe elements after deletion:");
for(i=0;i<MAX;i++)
printf("%d\t",p->arr[i]);
addq(p,21);
addq(p,17);
addq(p,18);
addq(p,14);
addq(p,20);
for(i=0;i<MAX;i++)
printf("%d\t",p->arr[i]);
addq(p,32);
addq(p,35);
addq(p,38);
for(i=0;i<MAX;i++)
printf("%d\t",p->arr[i]);
addq(p,40);
getch();
}
//adds an element to the queue
if(((p->rear==MAX-1)&&(p->front==0))||(p->rear+1==p->front))
printf("\nQueue is full\n");
return;
if(p->rear==MAX-1)
p->rear=0;
else
p->rear++;
p->arr[p->rear]=item;
if(p->front==-1)
p->front=0;
int data;
if(p->front==-1)
{
printf("\n Q is empty");
return;
data=p->arr[p->front];
p->arr[p->front]=NULL;
if(p->front==p->rear)
p->front=p->rear=-1;
else
if(p->front==MAX-1)
p->front=0;
else
p->front++;
APPLICATIONS OF QUEUES
a) Operating systems
b) Multiprogramming environments
c) Implementing various algorithms.
- interrupts to be serviced
This algorithm decides a smallest unit of time called a time quantum or time slice T.
This time slice is usually from 10 to 100ms. Hence, CPU starts servicing process P1.
P1 gets CPU time for T instant of time. Then CPU switches to P2 , and so on.
When CPU reaches the end of time quantum of Pn it returns to P1 and the same
Suppose if some process finishes its execution before its time quantum is finished the
Example :
process Burst-time
P1 5
P2 3