Professional Documents
Culture Documents
L2 - Stacks and Queues
L2 - Stacks and Queues
L2 - Stacks and Queues
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
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.
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
}
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:
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()*/
#include <stdio.h>
#define Blank ' '
#define Tab '\t'
#define MAX 50
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 ;
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;
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()*/
white_space(char symbol)
{
if( symbol == Blank || symbol == Tab || symbol == '\0')
return 1;
else
return 0;
} /*End of white_space()*/
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 */
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:
2. Indirect applications
Auxiliary data structure for algorithms
Component of other data structures
#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;
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
#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 . ");
}
} //end of main
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.