0% found this document useful (0 votes)
157 views45 pages

Unit - Ii: Stacks and Queues

The document discusses stacks and queues as data structures. It covers the basic concepts of stacks including LIFO operations of push and pop. It provides code examples to implement stacks using arrays. Key applications of stacks mentioned are recursive programming and expression evaluation. Queues are also introduced but not described in detail. Linked implementations of stacks and queues are briefly mentioned but not explained.

Uploaded by

G.Chinmayi
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
157 views45 pages

Unit - Ii: Stacks and Queues

The document discusses stacks and queues as data structures. It covers the basic concepts of stacks including LIFO operations of push and pop. It provides code examples to implement stacks using arrays. Key applications of stacks mentioned are recursive programming and expression evaluation. Queues are also introduced but not described in detail. Linked implementations of stacks and queues are briefly mentioned but not explained.

Uploaded by

G.Chinmayi
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 45

UNIT - II: STACKS AND QUEUES

STACKS: Introduction, Stack Operations, Applications,


QUEUES: Introduction, Operations on Queues, Circular Queues, Applications.
LINKED STACKS AND LINKED QUEUES: Introduction, Operations on Linked Stack
and Linked Queues, Dynamic Memory Management and Linked Stacks.

2.1 Introduction:

There are certain situations in computer science that one wants to restrict insertions and
deletions so that they can take place only at the beginning or the end of the list, not in the
middle. Two of such data structures that are useful are:
 Stack.
 Queue.
Linear lists and arrays allow one to insert and delete elements at any place in the list i.e., at
the beginning, at the end or in the middle.
2.2 STACK:
A stack is a list of elements in which an element may be inserted or deleted only at
one end, called the top of the stack. Stacks are sometimes known as LIFO (last in, first out)
lists.
As the items can be added or removed only from the top i.e. the last item to be
added to a stack is the first item to be removed.

The two basic operations associated with stacks are:

 Push: is the term used to insert an element into a stack.


 Pop: is the term used to delete an element from a stack.
“Push” is the term used to insert an element into a stack. “Pop” is the term used to
delete an element from the stack.
All insertions and deletions take place at the same end, so the last element added to
the stack will be the first element removed from the stack. When a stack is created, the
stack base remains fixed while the stack top changes as elements are added and removed.
The most accessible element is the top and the least accessible element is the bottom of the
stack.

2.2.1 Representation of Stack:

Let us consider a stack with 6 elements capacity. This is called as the size of the stack. The
number of elements to be added should not exceed the maximum size of the stack. If we
attempt to add new element beyond the maximum size, we will encounter a stack overflow
condition. Similarly, you cannot remove elements beyond the base of the stack. If such is
the case, we will reach a stack underflow condition.

When a element is added to a stack, the operation is performed by push(). Figure5.1 shows
the creation of a stack and addition of elements using push().
When an element is taken off from the stack, the operation is performed by pop(). Figure
5.2 shows a stack initially with three elements and shows the deletion of elements using
pop().

2.2.2 Implementation of push and pop operations:

Let STACK[1:n] be an array implementation of a stack and top be a variable recording the
current top of stack position. top is initialized to 0. Item is the element to be pushed into
the stack. n is the maximum capacity of the stack.

While implementation of stacks using arrays necessitates checking for


STACK_FULL/STACK_EMPTY Conditions during push/pop operations respectively.

Algorithm PUSH(STACK, n, top, item)


{
if(top=n) then
display “STACK_FULL”
else
{
top=top+1;
STACK[top]=item // store the item as top element of STACK.
}
}
Algorithm POP(STACK, top, item)
{
if(top=0) the
Display “ STACK_EMPTY
else
{
item=STACK[top]
top=top-1;
}
}
It is evident from the algorithms that to perform a single push/pop operation the time
complexity is O(1).

2.2.3 Source code for stack operations, using array:

# include <stdio.h>
# include <conio.h>
# include <stdlib.h>
# define MAX 6
int stack[MAX];
int top = 0;

int menu()
{
int ch;
clrscr();
printf("\n … Stack operations using ARRAY... ");
printf("\n -----------**********-------------\n");
printf("\n 1. Push ");
printf("\n 2. Pop ");
printf("\n 3. Display");
printf("\n 4. Quit ");
printf("\n Enter your choice: ");
scanf("%d", &ch);
return ch;
}

void display()
{
int i;
if(top == 0)
{
printf("\n\nStack empty..");
return;
}
else
{
printf("\n\nElements in stack:");
for(i = 0; i < top; i++)
printf("\t%d", stack[i]);
}
}

void pop()
{
if(top == 0)
{
printf("\n\nStack Underflow..");
return;
}
else
printf("\n\npopped element is: %d ", stack[--top]);
}

void push()
{
int data;
if(top == MAX)
{
printf("\n\nStack Overflow..");
return;
}
else
{
printf("\n\nEnter data: ");
scanf("%d", &data);
stack[top] = data;
top = top + 1;
printf("\n\nData Pushed into the stack");
}
}

void main()
{
int ch;
do
{
ch = menu();
switch(ch)
{
case 1:
push();
break;
case 2:
pop();
break;
case 3:
display();
break;
case 4:
exit(0);
}
getch();

} while(1);
}

2.2.4 Applications:
i. Recursive Programming
ii. Evaluation of Expressions

i.Recursive Programming:
Some computer programming languages allow a module or function to call itself. This
technique is known as recursion. In recursion, a function either calls itself directly or calls a
another function that in turn calls the original function.
Example − a function calling itself.
int function(int value)
{
if(value < 1)
return;
function(value - 1);
printf("%d ",value);
}
Example − a function that calls another function which in turn calls it again.

void main()
{
------
------
function();
------
-----
}
int function(int value)
{
if(value < 1)
return;
function(value - 1);
printf("%d ",value);
}
Properties
A recursive function can go infinite like a loop. To avoid infinite running of recursive
function, there are two properties that a recursive function must have −
 Base criteria − There must be at least one base criteria or condition, such that,
when this condition is met the function stops calling itself recursively.
 Progressive approach − The recursive calls should progress in such a way that
each time a recursive call is made it comes closer to the base criteria.
Implementation
Many programming languages implement recursion by means of stacks. Generally,
whenever a function (caller) calls another function (callee) or itself as callee, the caller
function transfers execution control to the callee. This transfer process may also involve
some data to be passed from the caller to the callee.
This implies, the caller function has to suspend its execution temporarily and resume later
when the execution control returns from the callee function. Here, the caller function needs
to start exactly from the point of execution where it puts itself on hold. It also needs the
exact same data values it was working on. For this purpose, an activation record (or stack
frame) is created for the caller function.
This activation record keeps the information about local variables, formal parameters,
return address and all information passed to the caller function.
Analysis of Recursion
One may argue why to use recursion, as the same task can be done with iteration. The first
reason is, recursion makes a program more readable and because of latest enhanced CPU
systems, recursion is more efficient than iterations.
Time Complexity
In case of iterations, we take number of iterations to count the time complexity. Likewise,
in case of recursion, assuming everything is constant, we try to figure out the number of
times a recursive call is being made. A call made to a function is Ο(1), hence the (n)
number of times a recursive call is made makes the recursive function Ο(n).

ii.Evaluation of Expressions:
An algebraic expression is a legal combination of operators and operands. Operand is the
quantity on which a mathematical operation is performed. Operand may be a variable like x,
y, z or a constant like 5, 4, 6 etc. Operator is a symbol which signifies a mathematical or
logical operation between the operands. Examples of familiar operators include +, -, *, /, ^
etc.

An algebraic expression can be represented using three different notations. They are infix,
postfix and prefix notations:
Infix: It is the form of an arithmetic expression in which we fix (place) the
arithmetic operator in between the two operands.
Example: (A + B) * (C - D)
Prefix: It is the form of an arithmetic notation in which we fix (place) the arithmetic
operator before (pre) its two operands. The prefix notation is called as polish
notation (due to the polish mathematician Jan Lukasiewicz in the year 1920).
Example: * + A B – C D
Postfix: It is the form of an arithmetic expression in which we fix (place) the arithmetic
operator after (post) its two operands. The postfix notation is called as suffix
notation and is also referred to reverse polish notation.
Example: A B + C D - *
The three important features of postfix expression are:
1. The operands maintain the same order as in the equivalent infix expression.
2. The parentheses are not needed to designate the expression un-ambiguously.
3. While evaluating the postfix expression the priority of the operators is no longer
relevant.

We consider five binary operations: +, -, *, / and $ or  (exponentiation). For these binary


operations, the following in the order of precedence (highest to lowest):

Operator Precedence Value

Exponentiation ($ or  or ^) Highest 3

*, / Next highest 2

+, - Lowest 1

4.3. Converting expressions using Stack:

Let us convert the expressions from one type to another. These can be done as follows:

1. Infix to postfix
2. Infix to prefix
3. Postfix to infix
4. Postfix to prefix
5. Prefix to infix
6. Prefix to postfix

4.3.1. Conversion from infix to postfix:

Procedure to convert from infix expression to postfix expression is as follows:


1. Scan the infix expression from left to right.
2. a) If the scanned symbol is left parenthesis, push it onto the stack.
b) If the scanned symbol is an operand, then place directly in the postfix
expression (output).

c) If the symbol scanned is a right parenthesis, then go on popping all the


items from the stack and place them in the postfix expression till we get
the matching left parenthesis.
d) If the scanned symbol is an operator, then go on removing all the
operators from the stack and place them in the postfix expression, if and
only if the precedence of the operator which is on the top of the stack is
greater than (or equal) to the precedence of the scanned operator and
push the scanned operator onto the stack otherwise, push the scanned
operator onto the stack.

Example 1:

Convert ((A – (B + C)) * D)  (E + F) infix expression to postfix form:

Symbol Postfix string Stack Remarks


( (
( ((
A A ((
- A ((-
( A ((-(
B AB ((-(
+ AB ((-(+
C ABC ((-(+
) ABC+ ((-
) ABC+- (
* ABC+- (*
D ABC+-D (*
) ABC+-D*
 ABC+-D* 
( ABC+-D* (
E ABC+-D*E (
+ ABC+-D*E (+
F ABC+-D*EF (+
) ABC+-D*EF+ 
End of The input is now empty. Pop the output symbols
string ABC+-D*EF+ from the stack until it is empty.

Example 2:

Convert a + b * c + (d * e + f) * g the infix expression into postfix form.

Symbol Postfix string Stack

a a

+ a +

b ab +

* ab +*

c abc +*

+ abc*+ +

( abc*+ +(

d abc*+d +(

* abc*+d +(*

e abc*+de +(*

+ abc*+de* +(+

f abc*+de*f +(+

) abc*+de*f+ +

* abc*+de*f+ +*

g abc*+de*f+g +*
End of a b c * + d e * f + g * The input is now empty. Pop the output symbols
string + from the stack until it is empty.

Example 3:

Convert the following infix expression A + B * C – D / E * H into its equivalent postfix


expression.

Symbol Postfix string Stack Remarks


A A
+ A +
B AB +
* AB +*
C ABC -
- ABC*+ -
D ABC*+D -
/ ABC*+D -/
E ABC*+DE -/
* ABC*+DE/ -*
H ABC*+DE/H -*
End of The input is now empty. Pop the output symbols from
string ABC*+DE/H*- the stack until it is empty.

Example 4:

Convert the following infix expression A + (B * C – (D / E  F) * G) * H into its equivalent


postfix expression.

Symbol Postfix string Stack Remarks


A A
+ A +(
( A +(
B AB +(*
* AB +(*
C ABC +(-
- ABC* +(-(
( ABC* +(-(
D ABC*D +(-(/
/ ABC*D +(-(/
E ABC*DE +(-(/
 ABC*DE +(-(/
F ABC*DEF +(-
) ABC*DEF/ +(-
* ABC*DEF/ +(-*
G ABC*DEF/G +(-*
) ABC*DEF/G*- +
* ABC*DEF/G*- +*
H ABC*DEF/G*-H* +*
End of A B C * D E F  / G * - H * The input is now empty. Pop the output
string + symbols from the stack until it is empty.

Conversion from infix to prefix:

The precedence rules for converting an expression from infix to prefix are identical. The only
change from postfix conversion is that traverse the expression from right to left and the
operator is placed before the operands rather than after them. The prefix form of a complex
expression is not the mirror image of the postfix form.

Example 1:

Convert the infix expression A + B - C into prefix expression.

Symbol Prefix String Stack Remarks

C C

- C -

B BC -

+ BC -+

A ABC -+
End of -+ABC The input is now empty. Pop the output symbols from the
string stack until it is empty.

Example 2:

Convert the infix expression (A + B) * (C - D) into prefix expression.

Symbol Prefix String Stack Remarks

) )

D D )

- D )-

C CD )-

( -CD

* -CD *

) -CD *)

B B-CD *)

+ B-CD *)+
A AB-CD *)+

( +AB–CD *
End of *+AB–CD The input is now empty. Pop the output symbols from the
string stack until it is empty.

Example 3:

Convert the infix expression A  B * C – D + E / F / (G + H) into prefix expression.

Symbol Prefix string Stack Remarks

) )

H H )

+ H )+
G GH )+

( +GH

/ +GH /

F F+GH /

/ F+GH //

E EF+GH //

+ //EF+GH +

D D//EF+GH +

- D//EF+GH +-

C CD//EF+GH +-

* CD//EF+GH +-*

B BCD//EF+GH +-*

 BCD//EF+GH +-*

A ABCD//EF+GH +-*
End of The input is now empty. Pop the output
+-*ABCD//EF+GH
string symbols from the stack until it is empty.

Conversion from postfix to infix:

Procedure to convert postfix expression to infix expression is as follows:


1. Scan the postfix expression from left to right.
2. If the scanned symbol is an operand, then push it onto the stack.

3. If the scanned symbol is an operator, pop two symbols from the stack and
create it as a string by placing the operator in between the operands and
push it onto the stack.
4. Repeat steps 2 and 3 till the end of the expression.
Example:

Convert the following postfix expression A B C * D E F ^ / G * - H * + into its equivalent


infix expression.

Symbol Stack Remarks


A A Push A
B A B Push B
C A B C Push C
Pop two operands and place the
* A (B*C) operator in between the
operands and push the string.
D A (B*C) D Push D
E A (B*C) D E Push E
F A (B*C) D E F Push F
Pop two operands and place the
A (B*C D (E^F)
^ operator in between the
)
operands and push the string.
Pop two operands and place the
/ A (B*C) (D/(E^F)) operator in between the
operands and push the string.
G A (B*C) (D/(E^F)) G Push G
Pop two operands and place the
A (B*C ((D/(E^F))*G)
* operator in between the
)
operands and push the string.
Pop two operands and place the
- A ((B*C) – ((D/(E^F))*G)) operator in between the
operands and push the string.
H A ((B*C) – ((D/(E^F))*G)) H Push H
Pop two operands and place the
* A (((B*C) – ((D/(E^F))*G)) * H) operator in between the
operands and push the string.
+ (A + (((B*C) – ((D/(E^F))*G)) * H))
End of
The input is now empty. The string formed is infix.
string

Conversion from postfix to prefix:


Procedure to convert postfix expression to prefix expression is as follows:
1. Scan the postfix expression from left to right.
2. If the scanned symbol is an operand, then push it onto the stack.

3. If the scanned symbol is an operator, pop two symbols from the stack and
create it as a string by placing the operator in front of the operands and push
it onto the stack.
5. Repeat steps 2 and 3 till the end of the expression.

Example:

Convert the following postfix expression A B C * D E F ^ / G * - H * + into its equivalent


prefix expression.
Symbol Stack Remarks
A A Push A
B A B Push B
C A B C Push C
Pop two operands and place the
* A *BC operator in front the operands
and push the string.
D A *BC D Push D
E A *BC D E Push E
F A *BC D E F Push F
Pop two operands and place the
^ A *BC D ^EF operator in front the operands
and push the string.
Pop two operands and place the
/ A *BC /D^EF operator in front the operands
and push the string.
G A *BC /D^EF G Push G
Pop two operands and place the
* A *BC */D^EFG operator in front the operands
and push the string.
Pop two operands and place the
- A - *BC*/D^EFG operator in front the operands
and push the string.
H A - *BC*/D^EFG H Push H
Pop two operands and place the
* A *- *BC*/D^EFGH operator in front the operands
and push the string.
+ +A*-*BC*/D^EFGH
End of
The input is now empty. The string formed is prefix.
string

Conversion from prefix to infix:

Procedure to convert prefix expression to infix expression is as follows:


1. Scan the prefix expression from right to left (reverse order).
2. If the scanned symbol is an operand, then push it onto the stack.

3. If the scanned symbol is an operator, pop two symbols from the stack and
create it as a string by placing the operator in between the operands and
push it onto the stack.
4 Repeat steps 2 and 3 till the end of the expression.

Example:

Convert the following prefix expression + A * - * B C * / D ^ E F G H into its equivalent


infix expression.

Symbol Stack Remarks


H H Push H
G H G Push G
F H G F Push F
E H G F E Push E
Pop two operands and place the
^ H G (E^F) operator in between the
operands and push the string.
D H G (E^F) D Push D
Pop two operands and place the
/ H G (D/(E^F)) operator in between the
operands and push the string.
Pop two operands and place the
* H ((D/(E^F))*G) operator in between the
operands and push the string.
C H ((D/(E^F))*G) C Push C
B H ((D/(E^F))*G) C B Push B
Pop two operands and place the
* H ((D/(E^F))*G) (B*C) operator in front the operands
and push the string.
Pop two operands and place the
- H ((B*C)-((D/(E^F))*G)) operator in front the operands
and push the string.
Pop two operands and place the
* (((B*C)-((D/(E^F))*G))*H) operator in front the operands
and push the string.
A (((B*C)-((D/(E^F))*G))*H) A Push A
Pop two operands and place the
+ (A+(((B*C)-((D/(E^F))*G))*H)) operator in front the operands
and push the string.
End of
The input is now empty. The string formed is infix.
string

Conversion from prefix to postfix:

Procedure to convert prefix expression to postfix expression is as follows:

1. Scan the prefix expression from right to left (reverse order).

2. If the scanned symbol is an operand, then push it onto the stack.

3. If the scanned symbol is an operator, pop two symbols from the stack and
create it as a string by placing the operator after the operands and push it
onto the stack.

6. Repeat steps 2 and 3 till the end of the expression.

Example:

Convert the following prefix expression + A * - * B C * / D ^ E F G H into its equivalent


postfix expression.
Symbol Stack Remarks
H H Push H
G H G Push G
F H G F Push F
E H G F E Push E
Pop two operands and place the
^ H G EF^ operator after the operands and
push the string.
D H G EF^ D Push D
Pop two operands and place the
/ H G DEF^/ operator after the operands and
push the string.
Pop two operands and place the
* H DEF^/G* operator after the operands and
push the string.
C H DEF^/G* C Push C
B H DEF^/G* C B Push B
Pop two operands and place the
H DEF^/G* BC
* operator after the operands and
*
push the string.
Pop two operands and place the
- H BC*DEF^/G*- operator after the operands and
push the string.
Pop two operands and place the
* BC*DEF^/G*-H* operator after the operands and
push the string.
A BC*DEF^/G*-H* A Push A
Pop two operands and place the
+ ABC*DEF^/G*-H*+ operator after the operands and
push the string.
End of
The input is now empty. The string formed is postfix.
string

ii. Evaluation of postfix expression:

The compiler finds it convenient to evaluate an expression in its postfix form. The virtues of
postfix form include elimination of parentheses which signify priority of evaluation and the
elimination of the need to observe rules of hierarchy, precedence and associativity during
evaluation of the expression. This implies that the evaluation of a postfix expression is
done by merely undertaking a left to right scan of the expression, pushing operands into a
stack and finally placing the output of the evaluated expression into the stack.

Algorithm EVAL_POSTFIX(E)
{
X=get_next_CHARACTER(E) //get the next character of expressions E.
CaseX of
X is an operand:
Push x into stack S
X is an operator:
Pop out required number of operands from
The stack S, evaluate the operator and push
the result into the stack S;
X=”$”:
Pop out the result from stack S.
End case
}
Example 1:

Evaluate the postfix expression: 6 5 2 3 + 8 * + 3 + *

Symbol Operand 1 Operand 2 Value Stack Remarks

6 6

5 6, 5

2 6, 5, 2
The first four symbols are
3 6, 5, 2, 3
placed on the stack.
Next a ‘+’ is read, so 3 and 2
+ 2 3 5 6, 5, 5 are popped from the stack
and their sum 5, is pushed
8 2 3 5 6, 5, 5, 8 Next 8 is pushed
Now a ‘*’ is seen, so 8 and 5
* 5 8 40 6, 5, 40 are popped as 8 * 5 = 40 is
pushed
Next, a ‘+’ is seen, so 40
+ 5 40 45 6, 45 and 5 are popped and 40 +
5 = 45 is pushed
3 5 40 45 6, 45, 3 Now, 3 is pushed
Next, ‘+’ pops 3 and 45 and
+ 45 3 48 6, 48 pushes 45 + 3 = 48 is
pushed
Finally, a ‘*’ is seen and 48
* 6 48 288 288 and 6 are popped, the result
6 * 48 = 288 is pushed

Example 2:

Evaluate the following postfix expression: 6 2 3 + - 3 8 2 / + * 2  3 +

Symbol Operand 1 Operand 2 Value stack

6 6

2 6, 2

3 6, 2, 3

+ 2 3 5 6, 5

- 6 5 1 1

3 6 5 1 1, 3

8 6 5 1 1, 3, 8
2 6 5 1 1, 3, 8, 2

/ 8 2 4 1, 3, 4

+ 3 4 7 1, 7

* 1 7 7 7

2 1 7 7 7, 2

 7 2 49 49

3 7 2 49 49, 3

+ 49 3 52 52

Program to evaluate a postfix expression:

# include <stdio.h>
# include <conio.h>
# include <math.h>
# define MAX 20

int isoperator(char ch)


{
if(ch == '+' || ch == '-' || ch == '*' || ch == '/' || ch == '^')
return 1;
else
return 0;
}

void main(void)
{
char postfix[MAX];
int val;
char ch;
int i = 0, top = 0;
float val_stack[MAX], val1, val2, res;
clrscr();
printf("\n Enter a postfix expression: ");
scanf("%s", postfix);
while((ch = postfix[i]) != '\0')
{
if(isoperator(ch) == 1)
{
val2 = val_stack[--top];
val1 = val_stack[--top];
switch(ch)
{
case '+':
res = val1 + val2;
break;
case '-':
res = val1 - val2;
break;
case '*':
res = val1 * val2;
break;
case '/':
res = val1 / val2;
break;
case '^':
res = pow(val1, val2);
break;
}
val_stack[top] = res;
}
else
val_stack[top] = ch-48; /*convert character digit to integer digit */
top++;
i++;
}
printf("\n Values of %s is : %f ",postfix, val_stack[0] );
getch();
}

2.2.5 Applications of stacks:

1. Stack is used by compilers to check for balancing of parentheses, brackets and


braces.
2. Stack is used to evaluate a postfix expression.
3. Stack is used to convert an infix expression into postfix/prefix form.
4. In recursion, all intermediate arguments and return values are stored on the
processor’s stack.
5. During a function call the return address and arguments are pushed onto a stack
and on return they are popped off.
6. Depth first search uses a stack data structure to find an element from a graph.

2.3 Queue:

A queue is another special kind of list, where items are inserted at one end called the rear
and deleted at the other end called the front. Another name for a queue is a “FIFO” or
“First-in-first-out” list.
The operations for a queue are analogues to those for a stack, the difference is that the
insertions go at the end of the list, rather than the beginning. We shall use the following
operations on queues:

 enqueue: which inserts an element at the end of the queue.


 dequeue: which deletes an element at the start of the queue.

2.3.1 Representation of Queue:

Let us consider a queue, which can hold maximum of five elements. Initially the queue is
empty.
Now, insert 11 to the queue. Then queue status will be:

Next, insert 22 to the queue. Then the queue status is:

Again insert another element 33 to the queue. The status of the queue is:

Now, delete an element. The element deleted is the element at the front of the queue. So
the status of the queue is:

Again, delete an element. The element to be deleted is always pointed to by the FRONT
pointer. So, 22 is deleted. The queue status is as follows:
Now, insert new elements 44 and 55 into the queue. The queue status is:

Next insert another element, say 66 to the queue. We cannot insert 66 to the queue as the
rear crossed the maximum size of the queue (i.e., 5). There will be queue full signal. The
queue status is as follows:

Now it is not possible to insert an element 66 even though there are two vacant positions in
the linear queue. To over come this problem the elements of the queue are to be shifted
towards the beginning of the queue so that it creates vacant position at the rear end. Then
the FRONT and REAR are to be adjusted properly. The element 66 can be inserted at the
rear end. After this operation, the queue status is as follows:

This difficulty can overcome if we treat queue position with index 0 as a position that comes
after position with index 4 i.e., we treat the queue as a circular queue.

2.3.2 Implementation of insert and delete operations on a queue


Let Q[1:n] be an array implementation of a queue. Let FRONT and REAR be variables
recording the front and rear positions of the queue. The FRONT variable points to a position
which is physically one less than the actual front of the queue. ITEM is the element to be
inserted into the queue. N is the maximum capacity of the queue. Both FRONT and REAR
are initialized to 0.
INSERT Operation:
Algorithm INSERTQ(Q,N,ITEM,REAR) // insert item ITEM into Q with capacity n
{
if(REAR=n) then
{
Display message “ QUEUE_FULL”
}
else
{
REAR=REAR+1;
Q[REAR}=item
}
}

It can be observed in above algorithm that addition of every new element into the queue
increments the REAR variable. However, before insertion, the condition whether the queue
is full is checked. This ensures that there is no overflow of elements in a queue.
DELETE Operation:
The delete operation is illustrated in the below algorithm. Though a deletion operation
automatically deletes the front element of the queue, the variable ITEM is used as an output
variable to store and perhaps destroy the value of the element removed.
Algorithm DELETEQ(Q,FRONT,REAR,ITEM)
{
if(FRONT=REAR) then
Display message “ QUEUE is EMPTY”
else
{
FRONT=FRONT+1;
ITEM=Q[FRONT]
}
}

Hence it is observed that in an array implementation of queues, with every insertion, REAR
moves away from FRONT and with every deleltion FRONT moves towards REAR. When the
queue is empty, FRONT=REAR is satisfied and when full, REAR=n (the maximum capacity of
the queue) is satisfied.
The time complexity to perform a single insert/delete operation in a linear queue O(1).

2.3.3 Source code Queue operations using array:

In order to create a queue we require a one dimensional array Q(1:n) and two variables
front and rear. The conventions we shall adopt for these two variables are that front is
always 1 less than the actual front of the queue and rear always points to the last element
in the queue. Thus, front = rear if and only if there are no elements in the queue. The initial
condition then is front = rear = 0.

The various queue operations to perform insert, deletion and display the elements in a
queue are as follows:

1. insertQ(): inserts an element at the end of queue Q.


2. deleteQ(): deletes the first element of Q.
3. displayQ(): displays the elements in the queue.

# include <stdio.h>
# include <conio.h>
# define MAX 6

int Q[MAX];
int front, rear;

void insertQ()
{
int data;
if(rear == MAX)
{
printf("\n Linear Queue is full");
return;
}
else
\ {
printf("\n Enter data: ");
scanf("%d", &data);
Q[rear] = data;
rear++;
printf("\n Data Inserted in the Queue ");
}
}
void deleteQ()
{
if(rear == front)
{
printf("\n\n Queue is Empty..");
return;
}
else
{
printf("\n Deleted element from Queue is %d", Q[front]);
front++;
}
}

void displayQ()
{
int i;
if(front == rear)
{
printf("\n\n\t Queue is Empty");
return;
}
else
{
printf("\n Elements in Queue are: ");
for(i = front; i < rear; i++)
{
printf("%d\t", Q[i]);
}
}
}

int menu()
{
int ch;
clrscr();
printf("\n \tQueue operations using ARRAY..");
printf("\n -----------**********-------------\n");
printf("\n 1. Insert ");
printf("\n 2. Delete ");
printf("\n 3. Display");
printf("\n 4. Quit ");
printf("\n Enter your choice: ");
scanf("%d", &ch);
return ch;
}

void main()
{
int ch;
do
{
ch = menu();
switch(ch)
{
case 1:
insertQ();
break;
case 2:
deleteQ();
break;
case 3:
displayQ();
break;
case 4:
return;
}
getch();
} while(1);
}

2.3.4 Circular Queue:

A more efficient queue representation is obtained by regarding the array Q[MAX] as circular.
Any number of items could be placed on the queue, so long as items were also being taken
off. This implementation of a queue is called a circular queue because it uses its storage
array as if it were a circle instead of a linear list.

There are two problems associated with linear queue. They are:

 Time consuming: linear time to be spent in shifting the elements to the beginning
of the queue.

 Signaling queue full: even if the queue is having vacant position.

For example, let us consider a linear queue status as follows:


Next insert another element, say 66 to the queue. We cannot insert 66 to the queue as the
rear crossed the maximum size of the queue (i.e., 5). There will be queue full signal. The
queue status is as follows:

This difficulty can overcome if we treat queue position with index zero as a position that
comes after position with index, four then we treat the queue as a circular queue.

In circular queue if we reach the end for inserting elements to it, it is possible to insert new
elements if the slots at the beginning of the circular queue are empty.

2.3.5 Representation of Circular Queue:

Let us consider a circular queue, which can hold maximum (MAX) of six elements. Initially
the queue is empty.

Now, insert 11 to the circular queue. Then circular queue status will be:
Insert new elements 22, 33, 44 and 55 into the circular queue. The circular queue status is:

Now, delete an element. The element deleted is the element at the front of the circular
queue. So, 11 is deleted. The circular queue status is as follows:

Again, delete an element. The element to be deleted is always pointed to by the FRONT
pointer. So, 22 is deleted. The circular queue status is as follows:
Again, insert another element 66 to the circular queue. The status of the circular queue is:

Now, insert new elements 77 and 88 into the circular queue. The circular queue status is:

Now, if we insert an element to the circular queue, as COUNT = MAX we cannot add the
element to circular queue. So, the circular queue is full.

2.3. 6 Implementation of insertion and deletion operations in a circular queue


Algorithms below illustrate the implementation of insert and delete operations in a circular
queue respectively. The circular movement of FRONT and REAR variables is implemented
using the mode function which is cyclical in nature. Also the array data structure CQ to
implement the queue is declared to be CQ[0:n-1] to facilitate the circular operation of
FRONT and REAR variables. As in linear queues, FRONT points to a position which is one less
than the actual front of the circular queue. Both FRONT and REAR are initialized O0. Note
that (n-1) is the actual physical capacity of the queue in spite of the array declaration as
[0:n-1].
INSERT Operation:
Algorithm INSERT_CQ(CQ, FRONT,REAR,n,ITEM)
{
REAR=(REAR+1) mod n;
if(FRONT=REAR) then
Display “ CIRCULAR QUEUE is FULL”
else
CQ[REAR]=ITEM;
}

Procedure DELETE_CQ(CQ, FRONT, REAR, n, ITEM)


{
if(FRONT=REAR) then
Display message “CIRCULAR QUEUE is empty”
else
{
FRONT=(FRONT+1) mod n;
ITEM=CQ[FRONT];
}
}
The time complexities of algorithms is O(1).
2.3.7 Source code Circular Queue operations, using array:

# include <stdio.h>
# include <conio.h>
# define MAX 6

int CQ[MAX];
int front = 0;
int rear = 0;
int count = 0;

void insertCQ()
{
int data;
if(count == MAX)
{
printf("\n Circular Queue is Full");
}
else
{
printf("\n Enter data: ");
scanf("%d", &data);
CQ[rear] = data;
rear = (rear + 1) % MAX;
count ++;
printf("\n Data Inserted in the Circular Queue ");
}
}

void deleteCQ()
{
if(count == 0)
{
printf("\n\nCircular Queue is Empty..");
}
else
{
printf("\n Deleted element from Circular Queue is %d ", CQ[front]);
front = (front + 1) % MAX;
count --;
}
}

void displayCQ()
{
int i, j;
if(count == 0)
{
printf("\n\n\t Circular Queue is Empty ");
}
else
{
printf("\n Elements in Circular Queue are: ");
j = count;
for(i = front; j != 0; j--)
{
printf("%d\t", CQ[i]);
i = (i + 1) % MAX;
}
}
}

int menu()
{
int ch;
clrscr();
printf("\n \t Circular Queue Operations using ARRAY..");
printf("\n -----------**********-------------\n");
printf("\n 1. Insert ");
printf("\n 2. Delete ");
printf("\n 3. Display");
printf("\n 4. Quit ");
printf("\n Enter Your Choice: ");
scanf("%d", &ch);
return ch;
}

void main()
{
int ch;
do
{
ch = menu();
switch(ch)
{
case 1:
insertCQ();
break;
case 2:
deleteCQ();
break;
case 3:
displayCQ();
break;
case 4:
return;
default:
printf("\n Invalid Choice ");
}
getch();
} while(1);
}

2.3.8 Applications of Queue:

1. It is used to schedule the jobs to be processed by the CPU.


2. When multiple users send print jobs to a printer, each printing job is kept in the
printing queue. Then the printer prints those jobs according to first in first out
(FIFO) basis.
3. Breadth first search uses a queue data structure to find an element from a graph.
2.4 LINKED STACKS AND LINKED QUEUES:

2.4.1 Linked List Implementation of Stack:

We can represent a stack as a linked list. In a stack push and pop operations are performed
at one end called top. We can perform similar operations at one end of list using top
pointer. The linked stack looks as shown in figure 6.9.1:
2.4.2 Algoriyhms:
Algorithm Push(TOP,ITEM)
{
Call GETNODE(X);
DATA(X)=ITEM; //frame node for ITEM
LINK(X)=TOP; //insert node x into stack
TOP=X; // reset top pointer
}

Note the absence of the STACK_FULL condition. The complexity of a push operation is O(1).

Algorithm POP(TOP, ITEM)


{
// pop element from stack and set ITEM to the element
If(TOP=0) then
Display stack is empty
Else
{
TEMP=TOP
ITEM=DATA(TOP)
TOP=LINK(TOP)
}
}
The time complexity of a pop operation is O(1).
2.4.3 Source code for stack operations, using linked list:

# include <stdio.h>
# include <conio.h>
# include <stdlib.h>

struct stack
{
int data;
struct stack *next;
};
void push();
void pop();
void display();
typedef struct stack node;
node *start=NULL;
node *top = NULL;

node* getnode()
{
struct stack *temp;
temp=(node *) malloc( sizeof(node)) ;
printf("\n Enter data ");
scanf("%d", &temp -> data);
temp -> next = NULL;
return temp;
}

void push(node *newnode)


{
node *temp;
if( newnode == NULL )
{
printf("\n Stack Overflow..");
return;
}
if(start == NULL)
{
start = newnode;
top = newnode;
}
else
{
temp = start;
while( temp -> next != NULL)
temp = temp -> next;
temp -> next = newnode;
top = newnode;
}
printf("\n\n\t Data pushed into stack");
}

void pop()
{
node *temp;
if(top == NULL)
{
printf("\n\n\t Stack underflow");
return;
}
temp = start;
if( start -> next == NULL)
{
printf("\n\n\t Popped element is %d ", top -> data);
start = NULL;
free(top);
top = NULL;
}
else
{
while(temp -> next != top)
{
temp = temp -> next;
}
temp -> next = NULL;
printf("\n\n\t Popped element is %d ", top -> data);
free(top);
top = temp;
}
}

void display()
{
node *temp;
if(top == NULL)
{
printf("\n\n\t\t Stack is empty ");
}
else
{
temp = start;
printf("\n\n\n\t\t Elements in the stack: \n");
printf("%5d ", temp -> data);
while(temp != top)
{
temp = temp -> next;
printf("%5d ", temp -> data);
}
}
}

char menu()
{
char ch;
clrscr();
printf("\n \tStack operations using pointers.. ");
printf("\n -----------**********-------------\n");
printf("\n 1. Push ");
printf("\n 2. Pop ");
printf("\n 3. Display");
printf("\n 4. Quit ");
printf("\n Enter your choice: ");
ch = getche();
return ch;
}

void main()
{
char ch;
node *newnode;
do
{
ch = menu();
switch(ch)
{
case '1' :
newnode = getnode();
push(newnode);
break;
case '2' :
pop();
break;
case '3' :
display();
break;
case '4':
return;
}
getch();
}while( ch != '4' );
}
2.4.4 Linked List Implementation of Queue:

We can represent a queue as a linked list. In a queue data is deleted from the front end and
inserted at the rear end. We can perform similar operations on the two ends of a list. We
use two pointers front and rear for our linked queue implementation.

The linked queue looks as shown in figure 6.10.1:

Algorithm INSERTQ(FRONT,REAR,ITEM)
{
Call GETNODE(X);
DATA(X)=ITEM;
LINK(X)=NULL; // node with ITEM is ready to be inserted into Q.
If(FRONT=0) then
FRONT=REAR=X; //if Q is empty then ITEM is the first element in the Queue.
Else
{
LINK(REAR)=X;
REAR=X;
}

}
Observe the absence of QUEUE_FULL condition in the insert procedure. The time complexity
of an insert operation o(1).

Algorithm DELETEQ(FRONT,ITEM)
{
If(FRONT=0) then
Display “ Queue is empty”
Else
{
TEMP=FRONT;
ITEM=DATA(TEMP);
FRONT=LINK(TEMP);
}
}
The time complexity of a delete operation is O(1).

2.4.5 Source code for queue operations using linked list:

# include <stdio.h>
# include <stdlib.h>
# include <conio.h>

struct queue
{
int data;
struct queue *next;
};

typedef struct queue node;


node *front = NULL;
node *rear = NULL;

node* getnode()
{
node *temp;
temp = (node *) malloc(sizeof(node)) ;
printf("\n Enter data ");
scanf("%d", &temp -> data);
temp -> next = NULL;
return temp;
}

void insertQ()
{
node *newnode;
newnode = getnode();
if(newnode == NULL)
{
printf("\n Queue Full");
return;
}
if(front == NULL)
{
front = newnode;
rear = newnode;
}
else
{
rear -> next = newnode;
rear = newnode;
}
printf("\n\n\t Data Inserted into the Queue..");
}

void deleteQ()
{
node *temp;
if(front == NULL)
{
printf("\n\n\t Empty Queue..");
return;
}
temp = front;
front = front -> next;
printf("\n\n\t Deleted element from queue is %d ", temp -> data);
free(temp);
}

void displayQ()
{
node *temp;
if(front == NULL)
{
printf("\n\n\t\t Empty Queue ");
}
else
{
temp = front;
printf("\n\n\n\t\t Elements in the Queue are: ");
while(temp != NULL )
{
printf("%5d ", temp -> data);
temp = temp -> next;
}
}
}

char menu()
{
char ch;
clrscr();
printf("\n \t..Queue operations using pointers.. ");
printf("\n\t -----------**********-------------\n");
printf("\n 1. Insert ");
printf("\n 2. Delete ");
printf("\n 3. Display");
printf("\n 4. Quit ");
printf("\n Enter your choice: ");
ch = getche();
return ch;
}

void main()
{
char ch;
do
{
ch = menu();
switch(ch)
{
case '1' :
insertQ();
break;
case '2' :
deleteQ();
break;
case '3' :
displayQ();
break;
case '4':
return;
}
getch();
} while(ch != '4');
}
2.4.6 Dynamic Memory Management and Linked stacks
Algorithm: implementation of procedure GETNODE(X) where AV is the pointer to the linked
stack implementation of AVAIL_SPACE.
Algorithm GETNODE(X)
{
If(AV=0) then
Display “ no free nodes”;
Else
{
X=AV;
AV=LINK(AV);// return the address X of the top node in AVAIL_SPACE.
}
}

Algorithm: implementation of procedure RETURN(X) where AV is the pointer to the linked


stack implementation of AVAIL_SPACE.
Algorithm RETURN(X)
{
LINK(X)=AV; // push node x into AVAIL_SPACE and reset AV.
AV=X;
}

2.4.7 Implementation of linked representation


It is emphasized here that nodes belonging to the reserved pool, that is nodes which are
currently in use, coexist with the nodes of the free pool in the same storage area. It is
therefore not uncommon to have a reserved node having a free node as its physically
contiguous neighbor. While the link fields of the free nodes, which in its simplest form is a
linked stack, keeps track of the free nodes in the list, the link fields of the reserved pool
similarly keep track of the reserved nodes in the list. Figure 7.5 illustrates a simple scheme
of reserved pool intertwined with the free pool in the memory storage.
2.5 Deque:
 
In the preceding section we saw that a queue in which we insert items at one end and from
which we remove items at the other end. In this section we examine an extension of the
queue, which provides a means to insert and remove items at both ends of the queue. This
data structure is a deque. The word deque is an acronym derived from double-ended queue.

Figure illustrates the basic deque operations. A deque provides four operations:

 enqueue_front: insert an element at front.


 dequeue_front: delete an element at front.
 enqueue_rear: insert element at rear.
 dequeue_rear: delete element at rear.
  
There are two variations of deque. They are:

 Input restricted deque (IRD)


 Output restricted deque (ORD)
An Input restricted deque is a deque, which allows insertions at one end but allows deletions
at both ends of the list.

An output restricted deque is a deque, which allows deletions at one end but allows
insertions at both ends of the list.

2.6 Priority Queue:

A priority queue is a collection of elements such that each element has been assigned a
priority and such that the order in which elements are deleted and processed comes from
the following rules:

1. An element of higher priority is processed before any element of lower priority.

2. two elements with same priority are processed according to the order in which
they were added to the queue.

A prototype of a priority queue is time sharing system: programs of high priority are
processed first, and programs with the same priority form a standard queue.

An efficient implementation for the Priority Queue is to use heap, which in turn can be used
for sorting purpose called heap sort.
2.7 Differentiate STACKS and QUEUES

S.No STACKS QUEUES


1. Stacks follow Last In First Out (LIFO) Queues following First In First Out
order. (FIFO) order.
2. In stacks, the last inserted object is first In queues, the object inserted first is
to come out. first deleted.
3. In a stack, an object is pushed on top of In a queue, new object is inserted at the
the collection during insertion operation. end.
4. Objects are inserted and removed at the Objects are inserted and removed from
same end. different ends.
5. In stacks only one pointer is used. It In queues, two different pointers are
points to the top of the stack. used for front and rear ends.
6. Stack operations are called push and pop Queue operations are called
enqueue(Insert) and dequeue(Delete).
7. If there is no value at the top, stack is If front=rear, queue is said to be empty
said to be empty
8. Stack can be implemented using both Queue also can be implemented using
arrays and linked lists. both arrays and linked lists
9. This has no sub types It has sub types such as: simple queue,
circular queue, double-ended queue,
priority queue

You might also like