Stack and Queue
Stack and Queue
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
Push - push (put) item onto stack
Pop - pop (get) item from stack
Initial Stack Push(8) Pop(8)
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.
Push() {
if there is room {
put an item on the top of the stack
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
}}
CreateStack()
{
remove existing items from the stack
initialise the stack to empty }
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:
static int stack[UPPERLIMIT];
int top= -1; /*stack is empty*/
..
main()
{
..
..
push(item);
..
..
}push(int item)
{
top = top + 1;
if(top < UPPERLIMIT)
stack[top] = item; /*step-1 & 2*/
else
cout<<"Stack Overflow";
}
Note:- In array implementation,we have taken TOP = -1 to signify the empty stack, as this
simplifies the implementation.
4.2. Array Implementation of Stacks: the POP operation
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:
static int stack[UPPPERLIMIT];
int top=-1;
....
main(){
....
poped_val = pop();
..}
int pop(){
int del_val = 0;
if(top == -1)
cout<<"Stack underflow"; /*step-1*/
else{
del_val = stack[top]; /*step-2*/
stack[top] = NULL;
top = top -1; }
return(del_val); }
Note: - Step-2:(b) signifies that the respective element has been deleted.
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 * +
The valuable aspect of RPN (Reverse Polish Notation or postfix )
Parentheses are unnecessary
Easy for a computer (compiler) to evaluate an arithmetic expression
Postfix (Reverse Polish Notation)
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.
Consider again the quadratic formula:
x = (-b+(b^2-4*a*c)^0.5)/(2*a)
In postfix form the formula becomes:
x b @ b 2 ^ 4 a * c * - 0.5 ^ + 2 a * / =
where @ represents the unary - operator.
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
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 + *
the first item is a value (6) so it is pushed onto the stack
the next item is a value (5) so it is pushed onto the stack
the next item is a value (2) so it is pushed onto the stack
the next item is a value (3) so it is pushed onto the stack
and the stack becomes
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.
ab
TOS=> +
TOS=> * abc
+
abc*+
TOS=> +
TOS=> *
abc*+de
(
+
TOS=> +
abc*+de*f
(
+
abc*+de*f+
+
TOS=>
abc*+de*f+
TOS=> *
g
+
abc*+de*f+
empty
g*+
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 track 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:
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
int FRONT =-1,REAR =-1;
int QUEUESIZE=0;
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);
}
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.
12 11
13
10
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:
- the total number of data in the queue
int FRONT =-1,REAR =-1;
int QUEUESIZE=0;
Example: Consider the following queue of persons where females have higher priority than
males (gender is the key to give priority).
Now the queue has data having equal priority and dequeue operation deletes the front
element like in the case of ordinary queues.
Thus, in the above example the implementation of the dequeue operation need to be
modified.
5.6. Application of Queues
i. Print server- maintains a queue of print jobs
Print()
{
EnqueuePrintQueue(Document)
}
EndOfPrint()
{
DequeuePrintQueue()
}
A tree is a set of nodes and edges that connect pairs of nodes that connect pairs of nodes. It is
an abstract model of a hierarchical structure. Rooted tree has the following structure:
One node distinguished as root.
Every node C except the root is connected from exactly other node P. P is C's parent,
and C is one of C's children.
There is a unique path from the root to the each node.
The number of edges in a path is the length of the path.
B E F G
C D H I J
K L M
H I J
K L M
Binary tree: a tree in which each node has at most two children called left child and right child.
Full binary tree: a binary tree where each node has either 0 or 2 children.
Balanced binary tree: a binary tree where each node except the leaf nodes has left and right
children and all the leaves are at the same level.
Complete binary tree: a binary tree in which the length from the root to any leaf node is either h
or h-1 where h is the height of the tree. The deepest level should also be
filled from left to right.
Binary search tree (ordered binary tree): a binary tree that may be empty, but if it is not empty it
satisfies the following.
Every node has a key and no two elements have the same key.
The keys in the right subtree are larger than the keys in the root.
The keys in the left subtree are smaller than the keys in the root.
The left and the right subtrees are also binary search trees.
10
6 15
4 8 14 18
7 12 16 19
11 13
struct DataModel
{
Declaration of data fields
DataModel * Left, *Right;
};
DataModel *RootDataModelPtr=NULL;