Chapter 5 Stack and Queue
Chapter 5 Stack and Queue
Chapter 5 Stack and Queue
Stacks
A simple data structure, in which insertion and deletion occur at the same end, is termed
(called) a stack. It is a LIFO (Last In First Out) structure.
The operations of insertion and deletion are called PUSH and POP
TOS=> 8
TOS=> 4 4 TOS=> 4
1 1 1
3 3 3
6 6 6
Our Purpose:
To develop a stack implementation that does not tie us to a particular data type or to a
particular implementation.
Implementation:
Stacks can be implemented both as an array (contiguous list) and as a linked list. We want a
set of operations that will work with either type of implementation: i.e. the method of
implementation is hidden and can be changed without affecting the programs that use them.
1|Page
Compiled by: Musa A.(MSc.)
else
give an error message
}
}
Pop()
{
if stack not empty {
return the value of the top item
remove the top item from the stack
}
else {
give an error message
}
}
Algorithm:
Step-1: Increment the Stack TOP by 1. Check whether it is always less than the Upper Limit of
the stack. If it is less than the Upper Limit go to step-2 else report -"Stack Overflow"
Step-2: Put the new element at the position pointed by the TOP
Implementation:
#define MAXSTACK 4
int top;
int stack[MAXSTACK];
int push(int stack[],int item)
{
if(top>MAXSTACK-1)
{
cout<<"stack overflow "<<endl;
}
else
{
top = top + 1;
stack[top] = item; }
2|Page
Compiled by: Musa A.(MSc.)
}
Note:- In array implementation,we have taken TOP = -1 to signify the empty stack, as this
simplifies the implementation.
POP is the synonym for delete when it comes to Stack. So, if you're taking an array as the stack,
remember that you'll return an error message, "Stack underflow", if an attempt is made to Pop an
item from an empty Stack. OK.
Algorithm
Step-1: If the Stack is empty then give the alert "Stack underflow" and quit; or else go to step-2
Step-2: a) Hold the value for the element pointed by the TOP
b) Put a NULL value instead
c) Decrement the TOP by 1
Implementation:
Note: - Step-2: signifies that the respective element has been deleted.
It’s very similar to the insertion operation in a dynamic singly linked list. The only difference is
that here you'll add the new element only at the end of the list, which means addition can happen
only from the TOP. Since a dynamic list is used for the stack, the Stack is also dynamic, means it
has no prior upper limit set. So, we don't have to check for the Overflow condition at all!
3|Page
Compiled by: Musa A.(MSc.)
If our top is at first end read that you write on your exercise book that is insertion
and deletion made at head or start end.
If top is at last end use the following.
It based on your arrangement since you change stack to linked list.
Algorithm
Implementation:
struct node{
int item;
struct node *next;
}
struct node *stack = NULL; /*stack is initially empty*/
struct node *top = stack;
main()
{
..
..
push(item);
..
}
push(int item)
{
if(stack == NULL) /*step-1*/
{
newnode = new node /*step-2*/
newnode -> item = item;
newnode -> next = NULL;
stack = newnode;
4|Page
Compiled by: Musa A.(MSc.)
top = stack;
}
else
{
newnode = new node; /*step-3*/
newnode -> item = item;
newnode -> next = NULL;
top ->next = newnode;
top = newnode; /*step-4*/
}
}
Applications of Stacks
Evaluation of Algebraic Expressions
e.g. 4 + 5 * 5
simple calculator: 45
Question:
Can we develop a method of evaluating arithmetic expressions without having to ‘look
ahead’ or ‘look back’? i.e. consider the quadratic formula:
x = (-b+(b^2-4*a*c)^0.5)/(2*a)
5|Page
Compiled by: Musa A.(MSc.)
In it’s current form we cannot solve the formula without considering the ordering of the
parentheses. i.e. we solve the innermost parenthesis first and then work outwards also
considering operator precedence. Although we do this naturally, consider developing an
algorithm to do the same . . . . . . possible but complex and inefficient. Instead . . . .
Computers solve arithmetic expressions by restructuring them so the order of each calculation is
embedded in the expression. Once converted an expression can then be solved in one pass.
Types of Expression
The normal (or human) way of expressing mathematical expressions is called infix form, e.g.
4+5*5. However, there are other ways of representing the same expression, either by writing all
operators before their operands or after them,
e.g.: 4 5 5 * +
+4*55
This method is called Polish Notation (because this method was discovered by the Polish
mathematician Jan Lukasiewicz).
When the operators are written before their operands, it is called the prefix form
e.g. + 4 * 5 5
When the operators come after their operands, it is called postfix form (suffix form or reverse
polish notation)
e.g. 4 5 5 * +
6|Page
Compiled by: Musa A.(MSc.)
Postfix notation arises from the concept of post-order traversal of an expression tree (see Weiss
p. 93 - this concept will be covered when we look at trees).
For now, consider postfix notation as a way of redistributing operators in an expression so that
their operation is delayed until the correct time.
Notice the order of the operands remain the same but the operands are redistributed in a non-
obvious way (an algorithm to convert infix to postfix can be derived).
Purpose
The reason for using postfix notation is that a fairly simple algorithm exists to evaluate such
expressions based on using a stack.
Postfix Evaluation
7|Page
Compiled by: Musa A.(MSc.)
Algorithm
initialise stack to empty;
while (not end of postfix expression) {
get next postfix item;
if(item is value)
push it onto the stack;
else if(item is binary operator) {
pop the stack to x;
pop the stack to y;
perform y operator x;
push the results onto the stack;
} else if (item is unary operator) {
pop the stack to x;
perform operator(x);
push the results onto the stack
}
}
The single value on the stack is the desired result.
Unary operators: unary minus, square root, sin, cos, exp, etc.,
So for 6 5 2 3 + 8 * + 3 + *
8|Page
Compiled by: Musa A.(MSc.)
TOS=> 3
So next a '+' is read (a binary operator), so 3 and 2 are popped from the stack and their
sum '5' is pushed onto the stack:
TOS=> 5
TOS=> 8
5 TOS=> 40
5 5
6 6
9|Page
Compiled by: Musa A.(MSc.)
Next the operator + followed by 3:
TOS=> 3
TOS=> 45 45
6 6
TOS=> 48
TOS=> 288
10 | P a g e
Compiled by: Musa A.(MSc.)
Now there are no more items and there is a single value on the stack, representing the final
answer 288.
Note the answer was found with a single traversal of the postfix expression, with the stack being
used as a kind of memory storing values that are waiting for their operands.
Algorithm
11 | P a g e
Compiled by: Musa A.(MSc.)
Operator Precedence (for this algorithm):
2:/*
1:+-
The algorithm immediately passes values (operands) to the postfix expression, but remembers
(saves) operators on the stack until their right-hand operands are fully translated.
12 | P a g e
Compiled by: Musa A.(MSc.)
eg., consider the infix expression a+b*c+(d*e+f)*g
Stack Output
ab
TOS=> +
TOS=> * abc
abc*+
TOS=> +
TOS=> *
abc*+de
(
TOS=> +
abc*+de*f
(
abc*+de*f+
13 | P a g e
Compiled by: Musa A.(MSc.)
TOS=> +
TOS=> * abc*+de*f+g
abc*+de*f+g*
empty +
14 | P a g e
Compiled by: Musa A.(MSc.)
Queue
a data structure that has access to its data at the front and rear.
operates on FIFO (Fast In First Out) basis.
uses two pointers/indices to keep tack of information/data.
has two basic operations:
o enqueue - inserting data at the rear of the queue
o dequeue – removing data at the front of the queue
dequeue enqueue
Front Rear
Example:
Operation Content of queue
Enqueue(B) B
Enqueue(C) B, C
Dequeue() C
Enqueue(G) C, G
Enqueue (F) C, G, F
Dequeue() G, F
Enqueue(A) G, F, A
Dequeue() F, A
Analysis:
Consider the following structure: int Num[MAX_SIZE];
We need to have two integer variables that tell:
- the index of the front element
- the index of the rear element
We also need an integer variable that tells:
- the total number of data in the queue
15 | P a g e
Compiled by: Musa A.(MSc.)
To enqueue data to the queue
O check if there is space in the queue
REAR<MAX_SIZE-1 ?
Yes: - Increment REAR
- Store the data in Num[REAR]
- Increment QUEUESIZE
FRONT = = -1?
Yes: - Increment FRONT
No: - Queue Overflow
To dequeue data from the queue
o check if there is data in the queue
QUEUESIZE > 0 ?
Yes: - Copy the data in Num[FRONT]
- Increment FRONT
- Decrement QUEUESIZE
No: - Queue Underflow
Implementation:
const int MAX_SIZE=100;
int FRONT =-1, REAR =-1;
int QUEUESIZE = 0;
void enqueue(int x)
{
if(Rear<MAX_SIZE-1)
{
REAR++;
Num[REAR]=x;
QUEUESIZE++;
if(FRONT = = -1)
FRONT++;
}
else
cout<<"Queue Overflow";
}
int dequeue()
{
int x;
if(QUEUESIZE>0)
{
x=Num[FRONT];
FRONT++;
QUEUESIZE--;
}
else
cout<<"Queue Underflow";
return(x);
}
16 | P a g e
Compiled by: Musa A.(MSc.)
Circular array implementation of enqueue and dequeue operations
A problem with simple arrays is we run out of space even if the queue never reaches the size of
the array. Thus, simulated circular arrays (in which freed spaces are re-used to store data) can be
used to solve this problem.
The circular array implementation of a queue with MAX_SIZE can be simulated as follows:
12 11
13
10
9
MAX_SIZE - 1 8
0 7
1 6
2 5
3 4
Analysis:
Consider the following structure: int Num[MAX_SIZE];
We need to have two integer variables that tell:
- the index of the front element
- the index of the rear element
We also need an integer variable that tells:
17 | P a g e
Compiled by: Musa A.(MSc.)
- the total number of data in the queue
int FRONT =-1,REAR =-1;
int QUEUESIZE=0;
Implementation:
#define MAXSIZE 6
void Inqueue(int queue[],int front,int rear,int item,int n)
{
if((front==MAXSIZE-1 || front==0) && (rear==MAXSIZE-1))
{
cout<<"queue is full "<<endl;
}
else
{
if(front==-1)
{
front++;
}
else if(rear==MAXSIZE-1)
{
rear =-1;
rear++;
queue[rear]=item;
}
else
{
18 | P a g e
Compiled by: Musa A.(MSc.)
rear++;
queue[rear]=item;
}
for(int i=0;i<n;i++){
cout<<queue[i];}
}
}
#define MAXSIZE 6
int dequeue(int queue[], int front,int rear,int n)
{
int removeditem;
if(front==-1)
{
cout<<"queue is empty "<<endl;
}
else
{
if(front==rear)
{
removeditem=queue[front];
queue[front]=NULL;
front=-1;
rear=-1;
//return removeditem;
}
else if(front==MAXSIZE-1)
{
removeditem=queue[front];
queue[front]=NULL;
front=0;
}
else
{
removeditem=queue[front];
queue[front]=NULL;
front++;
//return removeditem;
}
cout<<"item removed is "<<removeditem<<"\n";
}
}
Front Rear
Example: Consider the following queue of persons where females have higher priority
than males (gender is the key to give priority).
20 | P a g e
Compiled by: Musa A.(MSc.)
Thus, in the above example the implementation of the dequeue operation need to be
modified.
Demerging Queues
- is the process of creating two or more queues from a single queue.
- used to give priority for some groups of data
Example: The following two queues can be created from the above priority queue.
Aster Meron Abebe Alemu Belay Kedir Yonas
Female Female Male Male Male Male Male
Algorithm:
create empty females and males queue
while (PriorityQueue is not empty)
{
Data=DequeuePriorityQueue(); // delete data at the front
if(gender of Data is Female)
EnqueueFemale(Data);
else
EnqueueMale(Data);
}
Merging Queues
- is the process of creating a priority queue from two or more queues.
- the ordinary dequeue implementation can be used to delete data in the newly created
priority queue.
Example: The following two queues (females queue has higher priority than the males
queue) can be merged to create a priority queue.
Aster Meron Abebe Alemu Belay Kedir Yonas
Female Female Male Male Male Male Male
Algorithm:
21 | P a g e
Compiled by: Musa A.(MSc.)
Example: Consider the following priority queues and suppose large numbers represent
high priorities.
ABC CDE DEF FGH HIJ
52 41 35 16 12
Application of Queues
22 | P a g e
Compiled by: Musa A.(MSc.)