L2 - Stacks and Queues

You might also like

Download as pdf or txt
Download as pdf or txt
You are on page 1of 18

STACK

A stack is a linear data structure for collection of items , with the restriction that items can be
added one at a time and can only be removed in the reverse order in which they were added (i-e Last In
First Out LIFO). The last item represents the top of the stack.
Such a stack resembles a stack of trays in a cafeteria, or stack of boxes. Only the top tray can be
removed from the stack and it is the last one that was added to the stack. A tray can be removed only if
there are some trays on the stack, and a tray can be added only if there is enough room to hold more trays.
The common operations associated with a stack are as follows:
1. push: adds a new item on top of a stack.
2. pop: removes the item on the top of a stack
3. isEmpty: Check to see if the stack is empty
4. isFull: Check to see if stack is already full
5. returnTop: Indicate which item is at the top
Applications:
A stack is very useful in situations when data have to be stored and then retrieved in the
reverse order.

1. Function Calls

2. convert an infix expression into postfix form.

e.g. Infix Postfix


a+b*c abc*+
(a+b)*c ab+c*
(a + b) * (c – d) ab+cd-*

3. Evaluation of arithmetic expressions


e.g
5 * 3 +2 + 6 * 4 = 5 3 * 2 + 6 4 * + = 41

Infix to postfix conversion:


A stack can also be used to convert an infix expression in standard form into postfix form.
Infix: operator is between operands A + B
Postfix : operator follows operands A B +

We shall assume that the expression is a legal one (i.e. it is possible to evaluate it).
1. When an operand is read, it will be placed on output list (printed out straight away).
2. The operators are pushed on a stack. However, if the priority of the top operator in the stack is higher
than the operator being read, then it will be put on output list, and the new operator pushed on to the
stack.

The priority is assigned as follows.


1. ( Left parenthesis in the expression
2. * /
3. + –
4. ( Left parenthesis inside the stack
The association is assumed to be left to right. The left parenthesis has the highest priority when it is read
from the expression, but once it is on the stack, it assumes the lowest priority.

Algorithm:
while there are more characters in the input
{
Read next symbol ch in the given infix expression.
If ch is an operand put it on the output.
If ch is an operator i.e.* , /, +, -, or (
{
If stack is empty
push ch onto stack;
Else
check the item op at the top of the stack;
end
while (more items in stack && priority(ch)<= priority(op))
{
pop op and append it to the output, provided it
is not an open parenthesis
op = top element of stack
}
push ch onto stack
}

If ch is right parenthesis ‘)’


Pop items from stack until left parenthesis is reached
Pop left parenthesis and discard both left and right parenthesis
}/* now no more characters in the infix expression*/

Pop remaining items in the stack to the output.


Graphical representation:
e-g 1

Resulting Postfix Expression : M P K – T * +

E.g 2 convert 2*3/(2-1)+5*(4-1) into Postfix expression

e.g 3:
a + (( b * c ) / d )
a + ( ( b c * ) /d )
(precedence of * and / are same and they are left associative)
a+(bc*d/)
abc*d/+
Evaluating a Postfix Expression
We can evaluate a postfix expression using a stack.
1. Each operator in a postfix string corresponds to the previous two operands .
2. Each time we read an operand we push it onto a stack.
3. When we reach an operator its associated operands (the top two elements on the stack ) are
popped out from the stack.
4. We then perform the indicated operation on them and push the result on top of the stack so that it
will be available for use as one of the operands for the next operator .
Implementation of stacks using arrays:

/* Program of stack using array*/


#include <STDIO.H>
#define MAX 50

int top = -1;


int stack_arr[MAX];

main()
{
int choice;
while(1)
{

switch(choice)
{
case 1 :
push();
break;
case 2:
pop();
break;
case 3:
display();
break;
case 4:
exit(1);
default:
printf("Wrong choice\n");
}/*End of switch*/
}/*End of while*/
}/*End of main()*/

push()
{
int pushed_item;
if(top = = (MAX-1))
printf("Stack Overflow\n");
else
{
printf("Enter the item to be pushed in stack : ");
scanf("%d",&pushed_item);
top=top+1;
stack_arr[top] = pushed_item;
}
}/*End of push()*/

pop()
{
if(top = = -1)
printf("Stack Underflow\n");
else
{
printf("Popped element is : %d\n",stack_arr[top]);
top=top-1;
}
}/*End of pop()*/

display()
{
int i;
if(top = = -1)
printf("Stack is empty\n");
else
{
printf("Stack elements :\n");
for(i = top; i >=0; i--)
printf("%d\n", stack_arr[i] );
}
}/*End of display()*/

/* Program for conversion of infix to postfix and evaluation of


postfix. It will take only single digit in expression */

#include <stdio.h>
#define Blank ' '
#define Tab '\t'
#define MAX 50

long int pop ();


long int eval_post();
char infix[MAX], postfix[MAX];
long int stack[MAX];
int top;

main()
{
long int value;
top = 0;
printf("Enter infix : ");
gets(infix);
infix_to_postfix(); // calling infix to postfix conversion function
printf("Postfix : %s\n",postfix);
value=eval_post(); // caling the function for evaluating the expression
printf("Value of expression : %ld\n",value);
printf("Want to continue(y/n) : ");
}/*End of main()*/

infix_to_postfix()
{
int i,p=0,type,precedence;
char next ;

for(i=0; infix[i]!=’\0’; i++)


{

if( !white_space(infix[i]))
{
switch(infix[i])
{
case '(':
push(infix[i]);
break;

case ')':
while((next = pop()) != '(' )
postfix[p++] = next;
break;
case '+':
case '-':
case '*':
case '/':
case '%':
case '^':
precedence = prec(infix[i]);
while(top!=len && precedence<= prec(stack[top]))
postfix[p++] = pop();
push(infix[i]);
break;

default: /*if an operand comes */


postfix[p++] = infix[i];
} /*End of switch */
} /*End of if */
} /*End of for */

while(top!=len)
postfix[p++] = pop();
postfix[p] = '\0' ; /*End postfix with'\0' to make it a string*/
} /*End of infix_to_postfix()*/
/* This function returns the precedence of the operator */

prec(char symbol )
{
switch(symbol)
{
case '(':
return 0;
case '+':
case '-':
return 1;
case '*':
case '/':
case '%':
return 2;
case '^':
return 3;
} /*End of switch*/
} /*End of prec()*/

push(long int symbol)


{
if(top > MAX)
{
printf("Stack overflow\n");
exit(1);
}
else
{
top=top+1;
stack[top] = symbol;
}
} /*End of push()*/

long int pop()


{
if (top == -1 )
{
printf("Stack underflow \n");
exit(2);
}
else
return (stack[top--]);
} /*End of pop()*/
// function to check the symbol is a blank space or not

white_space(char symbol)
{
if( symbol == Blank || symbol == Tab || symbol == '\0')
return 1;
else
return 0;
} /*End of white_space()*/

// function to evaluate an expression


long int eval_post()
{
long int a,b,temp,result,len;
int i;
len=strlen(postfix);
postfix[len]='#';

for(i=0;postfix[i]!='#';i++)
{
if(postfix[i]<='9' && postfix[i]>='0')
push( postfix[i]-48 );
else
{
a=pop();
b=pop();

switch(postfix[i])
{
case '+':
temp=b+a;
break;
case '-':
temp=b-a;
break;
case '*':
temp=b*a;
break;
case '/':
temp=b/a;
break;
case '%':
temp=b%a;
break;
case '^':
temp=pow(b,a);
} /*End of switch */
push(temp);
} /*End of else*/
} /*End of for */
result=pop();
return result;
} /*End of eval_post */

Program to convert decimal number to binary number

/* Program of stack using array*/


#include <STDIO.H>
#define MAX 50

int top = -1;


int stack_arr[MAX];

main()
{
int dec;
printf(“ enter a decimal number\n”)
scanf(“%d”,&dec);
while(n>0)
{
rem = dec/2;
push(rem);
dec=dec/2;
}
printf(“binary number : \n”);
while(top!=-1)
{
b=pop();
printf(“%d”,b)
}
}
Queue

A queue is simply a waiting line that grows by adding elements to its end and shrinks by removing
elements from the front. Compared to stack, it reflects the more commonly used maxim in real-world,
namely, “first come, first served”. Waiting lines in supermarkets, banks, food counters are common
examples of queues.

Definition:
It is a list from which items may be deleted at one end (front) and into which items may be
inserted at the other end (rear). It is also referred to as a first-in-first-out (FIFO) data structure.

Operations on Queue:
1. enqueue (q, x)
inserts item x at the rear of the queue q
2. x = dequeue (q)
removes the front element from q and returns its value.
3. isEmpty(q)
Check to see if the queue is empty.
4. isFull(q)
checks to see if there is space to insert more items in the queue.

Queue overflow results from trying to add an element onto a full queue and queue underflow happens
when trying to remove an element from an empty queue.

Applications of Queues:

Queues have many applications in computer systems:


1. Direct application
 Handling jobs in a single processor computer
 print spooling
 transmitting information packets in computer networks

2. Indirect applications
 Auxiliary data structure for algorithms
 Component of other data structures

Program for Queue implementation through Array:

#include <stdio.h>
#include<ctype.h>
# define MAXSIZE 200
int q[MAXSIZE];
int front=-1;
int rear = -1;
void enqueue(int);
int dequeue();

void main()
{
int choice=1,i,num;

while(choice ==1)
{
printf("
MAIN MENU:
1.Add element to queue
2.Delete element from the queue ");
scanf("%d",& choice);

switch(choice)
{
case 1:
printf("Enter the data... ");
scanf("%d",&num);
enqueue(num);
break;
case 2:
i=dequeue();
printf("dequeued value is %d ",i);
break;
default:
printf("Invalid Choice ... ");
}
printf("Do you want to continue press 1 for yes, any other key to
exit");
scanf("%d" , & choice);
} //end of outer while
} //end of main

void enqueue(int a)
{
if(rear==MAXSIZE-1)
{
printf(" QUEUE FULL");
return;
}
else
{
rear = rear+1
q[rear]=a;
printf(" Value of rear = %d and the value of front is
%d",rear,front);
}
}

int dequeue()
{
int a;
if(front == rear)
{
printf(" QUEUE EMPTY");
return(0);
}
else
{
front = front+1
a=q[front];
}
return(a);
}
Circular Queue

Linear queues have a very big drawback that once the queue is FULL, even though we
delete few elements from the "front" and relieve some occupied space, we are not able to add
anymore elements, as the "rear" has already reached the Queue's rear most position.

Solution:
Once "rear" reaches the Queue's maximum limit, the "first" element will become the queue's
new "rear".
Now the Queue is not straight but circular. i-e once the Queue is full the "First" element of the
Queue becomes the "Rear" most element, if and only if the "Front" has moved forward;

Circular Queue Boundary Conditions:

 When the queue is empty, there is no front item and no rear item
 But if the front and rear variables point to valid array items, the difference between an
empty queue and a non-empty queue can be identified by
o Method 1: set front and/or rear to an out of range value (such as -1) to indicate
an empty queue
o Method 2: use a separate counter or Boolean flag to indicate an empty queue, in
which case (rear+1)%MAX == front while the queue is empty (note that this is
also the case when the queue is full!)
o Method 3: always keep at least one queue entry unused. Then an empty queue
has condition (rear+1)%MAX == front , while a full queue has condition
(rear+2)%MAX == front

Program for Circular Queue implementation through Array

#include <stdio.h>
#include<ctype.h>
# define MAXSIZE 8

int cq[MAXSIZE];
int front=-1, rear=-1;
void enqueue(int);
int dequeue( );

void main()
{
int choice=1,i,num;
clrscr();
while(choice = =1)
{
printf("
MAIN MENU: \n
1.Add element to Circular Queue \n
2.Delete element from the Circular Queue \n ");
scanf("%d",& choice);

switch(choice)
{
case 1:
printf("Enter the data... ");
scanf("%d",&num);
enqueue(num);
break;
case 2:
i=dequeue();
printf("Value returned from dequeue function is %d
",i);
break;
default:
printf("Invalid Choice . ");
}

printf(" Do you want to do more operations on Circular Queue


( 1 for yes, any other key to exit) ");
scanf("%d" , &choice);

} //end of outer while

} //end of main

void enqueue(int item)


{
rear=rear+1;
rear= (rear%MAX);
if(front = =rear)
{
printf("CIRCULAR QUEUE FULL");
return;
}
else
{
cq[rear]=item;
printf("Rear = %d Front = %d ",rear,front);
}
}

int dequeue()
{
int a;
if(front = = rear)
{
printf("CIRCULAR STACK EMPTY");
return (0);
}
else
{
front=front+1;
front = front%MAX;
a=cq[front];
printf("Rear = %d Front = %d ",rear,front);
return(a);
}
}

Applications:
1. Round robin scheduling

It is one of the oldest, simplest, and most widely used scheduling algorithms, designed
especially for time-sharing systems. A small unit of time, called timeslice or quantum, is
defined. All runnable processes are kept in a circular queue. The CPU scheduler goes around
this queue, allocating the CPU to each process for a time interval of one quantum. New
processes are added to the tail of the queue.

The CPU scheduler picks the first process from the queue, sets a timer to interrupt after one
quantum, and dispatches the process.

If the process is still running at the end of the quantum, the CPU is preempted and the process
is added to the tail of the queue.

If the process finishes before the end of the quantum, the process itself releases the CPU
voluntarily.

In either case, the CPU scheduler assigns the CPU to the next process in the ready queue. Every
time a process is granted the CPU, a context switch occurs, which adds overhead to the process
execution time.

You might also like