Thanks to visit codestin.com
Credit goes to www.scribd.com

0% found this document useful (0 votes)
13 views56 pages

UNIT 2 - DataStructures - Reg 21

This document provides an overview of stacks and queues, focusing on the Stack Abstract Data Type (ADT) and its operations, including push, pop, and peek. It explains stack implementation using both arrays and linked lists, along with exceptional conditions such as stack overflow and underflow. Additionally, it discusses applications of stacks in expression evaluation, syntax parsing, and balancing symbols.

Uploaded by

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

UNIT 2 - DataStructures - Reg 21

This document provides an overview of stacks and queues, focusing on the Stack Abstract Data Type (ADT) and its operations, including push, pop, and peek. It explains stack implementation using both arrays and linked lists, along with exceptional conditions such as stack overflow and underflow. Additionally, it discusses applications of stacks in expression evaluation, syntax parsing, and balancing symbols.

Uploaded by

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

CS8391 - DATA STRUCTURES

UNIT II STACKS AND QUEUES


Stack ADT – Operations – Applications – Balancing Symbols – Evaluating arithmetic
expressions- Infix to Postfix conversion – Function Calls – Queue ADT – Operations –
Circular Queue – DeQueue – Applications of Queues.

STACK ADT

A stack is an Abstract Data Type (ADT), commonly used in most programming


languages. It is named stack as it behaves like a real-world stack, for example – a deck of
cards or a pile of plates, Pile of books, Bunch of cups etc.

A stack is linear data structures, a container of elements that are inserted and removed
according to the last-in first-out (LIFO) principle. A stack is an ordered list of elements of
the same data type.for example – a deck of cards or a pile of plates, etc.

What is a Stack?

• Stack is a linear data structure in which the insertion and deletion operations are
performed at only one end.

• In a stack, adding and removing of elements are performed at a single position which is
known as "top". That means, a new element is added at top of the stack and an element
is removed from the top of the stack.

• In stack, the insertion and deletion operations are performed based on LIFO (Last In
First Out) principle.

• A stack is a ordered list of elements of same data type

• A stack is a Linear list.


Stack Representation

BASIC OPERATIONS

Stack operations may involve initializing the stack, using it and then de-initializing it.
Apart from these basic stuffs, a stack is used for the following two primary operations −

● push() − Pushing (storing) an element on the Top of the stack.


● pop() − Removing (accessing) an element from the Top of the stack.

For every push operation

1. Check for Full stack ( overflow ).


2. Increment Top by 1. (Top = Top + 1)
3. Insert the element X in the Top of the stack.

For every pop operation

1. Check for Empty stack ( underflow ).


2. Delete (pop) the Top element X from the stack
3. Decrement the Top by 1. (Top = Top - 1 )

Exceptional Conditions of stack

1.Stack Overflow
 An Attempt to insert an element X when the stack is Full, is said to
be stackoverflow.
 For every Push operation, we need to check this condition.
2.Stack Underflow
 An Attempt to delete an element when the stack is empty, is said to
be stackunderflow.
 For every Pop operation, we need to check this condition.
When data is PUSHed onto stack.

To use a stack efficiently, we need to check the status of stack as well. For the same
purpose, the following functionality is added to stacks −

● peek() − get the top data element of the stack, without removing it.
● isFull() − check if stack is full.
● isEmpty() − check if stack is empty.

At all times, we maintain a pointer to the last PUSHed data on the stack. As this pointer
always represents the top of the stack, hence named top. The top pointer provides the top
value of the stack without actually removing it.

IMPLEMENTATION OF STACK

Stack can be implemented in 2 ways.

 Static Implementation (Array implementation of Stack)


 Dynamic Implementation (Linked List Implementation of Stack)

ARRAY IMPLEMENTATION OF STACK

A stack data structure can be implemented using a one-dimensional array. But stack
implemented using array stores only a fixed number of data values. This implementation is
very simple. Just define a one dimensional array of specific size and insert or delete the
values into that array by using LIFO principle with the help of a variable called 'top'.
Initially, the top is set to -1. Whenever we want to insert a value into the stack, increment
the top value by one and then insert. Whenever we want to delete a value from the stack,
then delete the top value and decrement the top value by one.

Array Declaration of Stack


#define ArraySize 5 #define SIZE int
int S [ Array Size]; 5 int stack[SIZE];
or Top=-1;
int S [ 5 ];
STACK EMPTY OPERATION
 Initially Stack is Empty.
 With Empty stack Top pointer points to – 1.
It is necessary to check for Empty Stack before deleting (pop) an element from the stack

Routine to check whether stack is empty


int IsEmpty ( Stack S )
{
if( Top = = - 1 )
return(1);
}

STACK FULL OPERATION

 As we keep inserting the elements, the Stack gets filled with the elements.

 Hence it is necessary to check whether the stack is full or not before inserting a new
element into the stack.

Routine to check whether a stack is full

int IsFull ( Stack S )


{ if( Top = = Arraysize – 1 )
return(1);
}
PUSH OPERATION

 It is the process of inserting a new element at the Top of the stack.


 It takes two parameters. Push(X, S) the element X to be inserted at the Top of the
Stack S.
 Before inserting an Element into the stack, check for Full Stack.
 If the Stack is already Full, Insertion is not possible.
 Otherwise, Increment the Top pointer by 1 and then insert the element X at the Top
of the Stack.

Routine to push an element into the stack

void Push ( int X , Stack S )


{
if ( Top = = Arraysize - 1)
Error(“Stack is full!!Insertion is not possible”);
else
{ Top = Top + 1;
S [ Top ] =X;
}
}

POP OPERATION

 It is the process of deleting the Top element of the stack.


 It takes only one parameter. Pop(X).The element X to be deleted from the Top of
the Stack.
 Before deleting the Top element of the stack, check for Empty Stack.
 If the Stack is Empty, deletion is not possible.
 Otherwise, delete the Top element from the Stack and then decrement the Top
pointer by 1.

Routine to Pop the Top element of the stack

void Pop ( Stack S )


{
if ( Top = = - 1)
Error ( “Empty stack! Deletion not possible”);
else
{ X = S [ Top ] ;
Top = Top – 1 ;
}
}

RETURN TOP ELEMENT

 Pop routine deletes the Top element in the stack.

 If the user needs to know the last element inserted into the stack, then the user can
return the Top element of the stack.

 To do this, first check for Empty Stack.

 If the stack is empty, then there is no element in the stack.

 Otherwise, return the element which is pointed by the Top pointer in the Stack.
Routine to return top Element of the stack

int TopElement(Stack S)
{
if(Top==-1)
{
Error(“Empty stack!!No elements”); return 0;
}
else
return S[Top];
}

IMPLEMENTATION OF STACK USING ARRAY

#include<stdio.h>
int stack[100],choice,n,top,x,i;
void push(void);
void pop(void);
void display(void);
int main()
{
//clrscr();
top=-1;
printf("\n Enter the size of STACK[MAX=100]:");
scanf("%d",&n);
printf("\n\t STACK OPERATIONS USING ARRAY");
printf("\n\t--------------------------------");
printf("\n\t 1.PUSH\n\t 2.POP\n\t 3.DISPLAY\n\t 4.EXIT");
do
{
printf("\n Enter the Choice:");
scanf("%d",&choice);
switch(choice)
{
case 1:
{
push();
break;
}
case 2:
{
pop();
break;
}
case 3:
{
display();
break;
}
case 4:
{
printf("\n\t EXIT POINT ");
break;
}
default:
{
printf ("\n\t Please Enter a Valid Choice(1/2/3/4)");
}

}
}
while(choice!=4);
return 0;
}
void push()
{
if(top>=n-1)
{
printf("\n\tSTACK is over flow");

}
else
{
printf(" Enter a value to be pushed:");
scanf("%d",&x);
top++;
stack[top]=x;
}
}
void pop()
{
if(top<=-1)
{
printf("\n\t Stack is under flow");
}
else
{
printf("\n\t The popped elements is %d",stack[top]);
top--;
}
}
void display()
{
if(top>=0)
{
printf("\n The elements in STACK \n");
for(i=top; i>=0; i--)
printf("\n%d",stack[i]);
printf("\n Press Next Choice");
}
else
{
printf("\n The STACK is empty");
}

LINKED LIST IMPLEMENTATION OF STACK

The major problem with the stack implemented using an array is, it works only for a fixed
number of data values. That means the amount of data must be specified at the beginning of
the implementation itself. Stack implemented using an array is not suitable, when we don't
know the size of data which we are going to use. A stack data structure can be implemented
by using a linked list data structure. The stack implemented using linked lists can work for
an unlimited number of values. That means, a stack implemented using linked lists works
for the variable size of data. So, there is no need to fix the size at the beginning of the
implementation. The Stack implemented using linked list can organize as many data values
as we want.

In linked list implementation of a stack, every new element is inserted as 'top' element. That
means every newly inserted element is pointed by 'top'. Whenever we want to remove an
element from the stack, simply remove the node which is pointed by 'top' by moving 'top' to
its previous node in the list. The next field of the first element must be always NULL.

Example

In the above example, the last inserted node is 99 and the first inserted node is 25. The
order of elements inserted is 25, 32,50 and 99.

Stack Operations using Linked List


To implement a stack using a linked list, we need to set the following things before
implementing actual operations.

Step 1 - Include all the header files which are used in the program. And declare all the user
defined functions.

Step 2 - Define a 'Node' structure with two members data and next.

Step 3 - Define a Node pointer 'top' and set it to NULL.

Step 4 - Implement the main method by displaying the Menu with a list of operations and
make suitable function calls in the main method.

push(value) - Inserting an element into the Stack


We can use the following steps to insert a new node into the stack...

● Step 1 - Create a newNode with given value.


● Step 2 - Check whether stack is Empty (top == NULL)
● Step 3 - If it is Empty, then set newNode → next = NULL.
● Step 4 - If it is Not Empty, then set newNode → next = top.
● Step 5 - Finally, set top = newNode.
pop() - Deleting an Element from a Stack
We can use the following steps to delete a node from the stack...

● Step 1 - Check whether stack is Empty (top == NULL).


● Step 2 - If it is Empty, then display "Stack is Empty!!! Deletion is not possible!!!"
and terminate the function
● Step 3 - If it is Not Empty, then define a Node pointer 'temp' and set it to 'top'.
● Step 4 - Then set 'top = top → next'.
● Step 5 - Finally, delete 'temp'. (free(temp)).

display() - Displaying stack of elements


We can use the following steps to display the elements (nodes) of a stack...

● Step 1 - Check whether stack is Empty (top == NULL).


● Step 2 - If it is Empty, then display 'Stack is Empty!!!' and terminate the function.
● Step 3 - If it is Not Empty, then define a Node pointer 'temp' and initialize with top.
● Step 4 - Display 'temp → data --->' and move it to the next node. Repeat the same
until temp reaches to the first node in the stack. (temp → next != NULL).
● Step 5 - Finally! Display 'temp → data ---> NULL'.

Implementation of Stack using Linked List | C Programming


#include<stdio.h>
#include<conio.h>
struct Node
{
int data;
struct Node *next;
}*top = NULL;
void push(int);
void pop();
void display();
void main()
{
int choice, value;
clrscr();
printf("\n:: Stack using Linked List ::\n");
while(1){
printf("\n****** MENU ******\n");
printf("1. Push\n2. Pop\n3. Display\n4. Exit\n");
printf("Enter your choice: ");
scanf("%d",&choice);
switch(choice){
case 1: printf("Enter the value to be insert: ");
scanf("%d", &value);
push(value);
break;
case 2: pop(); break;
case 3: display(); break;
case 4: exit(0);
default: printf("\nWrong selection!!! Please try again!!!\n");
}
}
}
void push(int value)
{
struct Node *newNode;
newNode = (struct Node*)malloc(sizeof(struct Node));
newNode->data = value;
if(top == NULL)
newNode->next = NULL;
else
newNode->next = top;
top = newNode;
printf("\nInsertion is Success!!!\n");
}
void pop()
{
if(top == NULL)
printf("\nStack is Empty!!!\n");
else{
struct Node *temp = top;
printf("\nDeleted element: %d", temp->data);
top = temp->next;
free(temp);
}
}
void display()
{
if(top == NULL)
printf("\nStack is Empty!!!\n");
else{
struct Node *temp = top;
while(temp->next != NULL){
printf("%d--->",temp->data);
temp = temp -> next;
}
printf("%d--->NULL",temp->data);
}
}

OUTPUT
APPLICATIONS OF STACK

 Expression Evaluation: Stack is used to evaluate prefix, postfix and infix


expressions.

 A+b – Infix, AB+ - POST, +AB - PREFIX

 Expression Conversion: An expression can be represented in prefix, postfix or


infix notation. Stack can be used to convert one form of expression to another.

 Syntax Parsing: Many compilers use a stack for parsing the syntax of expressions,
program blocks etc. before translating into low level code.

 Backtracking: Suppose we are finding a path for solving maze problem. We choose
a path and after following it we realize that it is wrong. Now we need to go back to
the beginning of the path to start with new path. This can be done with the help of
stack.

 Parenthesis Checking: Stack is used to check the proper opening and closing of
parenthesis.

 String Reversal: Stack is used to reverse a string. We push the characters of string
one by one into stack and then pop character from stack.
 Function Call: Stack is used to keep information about the active functions or
subroutines.

 Tower of Hanoi
 8 Queen Problem

BALANCING SYMBOLS

Compilers check your programs for syntax errors, but frequently a lack of one
symbol (such as a missing brace or comment starter) will cause the compiler to spill out a
hundred lines of diagnostics without identifying the real error.

A useful tool in this situation is a program that checks whether everything is


balanced. Thus, every right brace, bracket, and parenthesis must correspond to their left
counterparts. The sequence [()] is legal, but [(]) is wrong. Obviously, it is not worthwhile
writing a huge program for this, but it turns out that it is easy to check these things. For
simplicity, we will just check for balancing of parentheses, brackets, and braces and ignore
any other character that appears.

The simple algorithm uses a stack and is as follows:

Make an empty stack. Read characters until end of file. If the character is an open anything,
push it onto the stack. If it is a close anything, then if the stack is empty report an error.
Otherwise, pop the stack. If the symbol popped is not the corresponding opening symbol,
then report an error. At end of file, if the stack is not empty report an error.

You should be able to convince yourself that this algorithm works. It is clearly linear and
actually makes only one pass through the input. It is thus on-line and quite fast. Extra work
can be done to attempt to decide what to do when an error is reported--such as identifying
the likely cause.

EVALUATING THE ARITHMETIC EXPRESSION


There are 3 types of Expressions
 Infix Expression
 Postfix Expression
 Prefix Expression

INFIX
The arithmetic operator appears between the two operands to which it is being
applied. If an operator is preceded and succeeded by an operand then such an expression is
termed infix expression.
It follows the scheme of <operand><operator><operand>
E.g., A/B+C

POSTFIX
The arithmetic operator appears directly after the two operands to which it applies.
Also called reverse polish notation.
If an operator is succeeded by both the operand then such an expression is termed
postfix expression.
It follows the scheme of <operand><operand><operator>
E.g., AB+

PREFIX
The arithmetic operator is placed before the two operands to which it applies. Also
called polish notation.

Advantage of Postfix Expression over Infix Expression

An infix expression is difficult for the machine to know and keep track of
precedence of operators. On the other hand, a postfix expression itself determines the
precedence of operators (as the placement of operators in a postfix expression depends upon
its precedence). Therefore, for the machine it is easier to carry out a postfix expression than
an infix expression.

Evaluating Arithmetic Expressions

1. Convert the given infix expression to Postfix expression


2. Evaluate the postfix expression using stack.
ALGORITHM TO CONVERT INFIX EXPRESSION TO POSTFIX EXPRESSION

Let, X is an arithmetic expression written in infix notation. This algorithm finds the
equivalent postfix expression Y.

1. Push “(“onto Stack, and add “)” to the end of X.

2. Scan X from left to right and repeat Step 3 to 6 for each element of X until the Stack
is empty.

3. If an operand is encountered, add it to Y.

4. If a left parenthesis is encountered, push it onto Stack.

5. If an operator is encountered ,then:

1. Repeatedly pop from Stack and add to Y each operator (on the top of Stack)
which has the same precedence as or higher precedence than operator.

2. Add operator to Stack.


[End of If]

6. If a right parenthesis is encountered ,then:

1. Repeatedly pop from Stack and add to Y each operator (on the top of Stack)
until a left parenthesis is encountered.

2. Remove the left Parenthesis.


[End of If]
[End of If]

7. END.

EXAMPLE FOR INFIX TO POSTFIX CONVERSION

EXAMPLE 1

Infix Expression: A+ (B*C-(D/E^F)*G)*H, where ^ is an exponential operator


Resultant Postfix Expression: ABC*DEF^/G*-H*+

ALGORITHM TO EVALUATE POSTFIX EXPRESSION

1. Read the postfix expression from left to right.


2. If an operand is encountered then push the element in the stack.
3. If an operator is encountered then pop the two operands from the stack and
then evaluate it.
4. Push back the result of the evaluation onto the stack.
5. Repeat it till the end of the expression.
EXAMPLE

Expression: 456*+
EXAMPLE 2

Infix expression:
K + L - M*N + (O^P) * W/U/V * T + Q
The final postfix expression KL+MN*-OP^W*U/V/T*+Q+.

Input Stack Postfix Expression


K
Expression K
+ +
L + KL
- - K L+
M - K L+ M
* -* K L+ M
N -* KL+MN
+ + K L + M N*
( +( KL+MN
N**--
O +( KL+MN*-O
^ +(^ K L + M N* - O
P +(^ K L + M N* - O P
) + K L + M N* - O P ^
* +* K L + M N* - O P ^
W +* K L + M N* - O P ^ W
/ +/ K L + M N* - O P ^ W *
U +/ K L + M N* - O P ^W*U
/ +/ K L + M N* - O P ^W*U/
V +/ KL + MN*-OP^W*U/V
* +* KL+MN*-OP^W*U/V/
T +* KL+MN*-OP^W*U/V/T
+ + KL+MN*-OP^W*U/V/T*
Q + KL+MN*-OP^W*U/V/T*Q
KL+MN*-OP^W*U/V/T*+
KL+MN*-OP^W*U/V/T*+Q+

Example: consider the obtained Postfix expression:- AB*CDE/-+

Operand Value
A 2
B 3
C 4
D 4
E 2

Char Read Stack


A 2
B 3
2
* 6
4
C 6
4
D 4
6
E
/ 2
4
6
- 2
6
+ 8
OUTPUT =8

FUNCTION CALLS

The algorithm to check balanced symbols suggests a way to implement function calls. The
problem here is that when a call is made to a new function, all the variables local to the
calling routine need to be saved by the system, since otherwise the new function will
overwrite the calling routine's variables. Furthermore, the current location in the routine
must be saved so that the new function knows where to go after it is done. The variables
have generally been assigned by the compiler to machine registers, and there are certain to
be conflicts (usually all procedures get some variables assigned to register #1), especially if
recursion is involved. The reason that this problem is similar to balancing symbols is that a
function call and function return are essentially the same as an open parenthesis and closed
parenthesis, so the same ideas should work.

When there is a function call, all the important information that needs to be saved, such as
register values (corresponding to variable names) and the return address (which can be
obtained from the program counter, which is typically in a register), is saved "on a piece of
paper" in an abstract way and put at the top of a pile. Then the control is transferred to the
new function, which is free to replace the registers with its values. If it makes other function
calls, it follows the same procedure. When the function wants to return, it looks at the
"paper" at the top of the pile and restores all the registers. It then makes the return jump.

Clearly, all of this work can be done using a stack, and that is exactly what happens in
virtually every programming language that implements recursion. The information saved is
called either an activation record or stack frame. The stack in a real computer frequently
grows from the high end of your memory partition downwards, and on many systems there
is no checking for overflow. There is always the possibility that you will run out of stack
space by having too many simultaneously active functions. Needless to say, running out of
stack space is always a fatal error.

In languages and systems that do not check for stack overflow, your program will crash
without an explicit explanation. On these systems, strange things may happen when your
stack gets too big, because your stack will run into part of your program. It could be the
main program, or it could be part of your data, especially if you have a big array. If it runs
into your program, your program will be corrupted; you will have nonsense instructions and
will crash as soon as they are executed. If the stack runs into your data, what is likely to
happen is that when you write something into your data, it will destroy stack information --
probably the return address -- and your program will attempt to return to some weird
address and crash.

In normal events, you should not run out of stack space; doing so is usually an indication of
runaway recursion (forgetting a base case). On the other hand, some perfectly legal and
seemingly innocuous program can cause you to run out of stack space. The routine in
Figure 3.54, which prints out a linked list, is perfectly legal and actually correct. It properly
handles the base case of an empty list, and the recursion is fine. This program can be proven
correct. Unfortunately, if the list contains 20,000 elements, there will be a stack of 20,000
activation records representing the nested calls of line 3. Activation records are typically
large because of all the information they contain, so this program is likely to run out of
stack space. (If 20,000 elements are not enough to make the program crash, replace the
number with a larger one.)

This program is an example of an extremely bad use of recursion known as tail recursion.
Tail recursion refers to a recursive call at the last line. Tail recursion can be mechanically
eliminated by changing the recursive call to a goto preceded by one assignment per function
argument. This simulates the recursive call because nothing needs to be saved -- after the
recursive call finishes, there is really no need to know the saved values. Because of this, we
can just go to the top of the function with the values that would have been used in a
recursive call. The program in Figure 3.55 shows the improved version. Keep in mind that
you should use the more natural while loop construction. The goto is used here to show how
a compiler might automatically remove the recursion.

Removal of tail recursion is so simple that some compilers do it automatically. Even so, it is
best not to find out that yours does not.
void /* Not using a header */

print_list( LIST L )

/*1*/ if( L != NULL )

/*2*/ print_element( L->element );

/*3*/ print_list( L->next );

Figure : A bad use of recursion: printing a linked list

void

print_list( LIST L ) /* No header */

top:

if( L != NULL )

print_element( L->element );

L = L->next;

goto top;

Figure: Printing a list without recursion; a compiler might do this (you should not)

Recursion can always be completely removed (obviously, the compiler does so in


converting to assembly language), but doing so can be quite tedious. The general strategy
requires using a stack and is obviously worthwhile only if you can manage to put only the
bare minimum on the stack. We will not dwell on this further, except to point out that
although nonrecursive programs are certainly generally faster than recursive programs, the
speed advantage rarely justifies the lack of clarity that results from removing the recursion.

QUEUE ADT

Queue is a linear data structure in which the insertion and deletion operations are performed
at two different ends. In a queue data structure, adding and removing elements are
performed at two different positions. The insertion is performed at one end and deletion is
performed at another end. In a queue data structure, the insertion operation is performed at a
position which is known as 'rear' and the deletion operation is performed at a position which
is known as 'front'. In queue data structure, the insertion and deletion operations are
performed based on FIFO (First In First Out).

The following operations are performed on a queue data structure:

1. enQueue(value) - (To insert an element into the queue)


2. deQueue() - (To delete an element from the queue)
3. display() - (To display the elements of the queue)

Exceptional Conditions
• Overflow: Attempting to insert an element into queue when it is full
• Underflow: Attempting to delete an element from queue when it is empty

Queue data structure can be implemented in two ways.


They are as follows...
Using Array
Using Linked List

QUEUE OPERATIONS USING ARRAY

Queue data structure using array can be implemented as follows...

Before we implement actual operations, first follow the below steps to create an empty
queue.

● Step 1 - Include all the header files which are used in the program and define a
constant 'SIZE' with specific value.
● Step 2 - Declare all the user defined functions which are used in queue
implementation.
● Step 3 - Create a one dimensional array with above defined SIZE (int queue[SIZE])
● Step 4 - Define two integer variables 'front' and 'rear' and initialize both with '-1'.
(int front = -1, rear = -1)
● Step 5 - Then implement main method by displaying menu of operations list and
make suitable function calls to perform operation selected by the user on queue.

enQueue(value) - Inserting value into the queue

In a queue data structure, enQueue() is a function used to insert a new element into the
queue. In a queue, the new element is always inserted at rear position. The enQueue()
function takes one integer value as a parameter and inserts that value into the queue. We can
use the following steps to insert an element into the queue...

● Step 1 - Check whether queue is FULL. (rear == SIZE-1)


● Step 2 - If it is FULL, then display "Queue is FULL!!! Insertion is not
possible!!!" and terminate the function.
● Step 3 - If it is NOT FULL, then increment rear value by one (rear++) and set
queue[rear] = value.
void enQueue(int value){
if(rear == SIZE-1)
printf("\nQueue is Full!!! Insertion is not possible!!!");
else{
if(front == -1)
front = 0;
rear++;
queue[rear] = value;
printf("\n Insertion success!!!");
}
}

deQueue() - Deleting a value from the Queue

In a queue data structure, deQueue() is a function used to delete an element from the queue.
In a queue, the element is always deleted from front position. The deQueue() function does
not take any value as parameter. We can use the following steps to delete an element from
the queue...

● Step 1 - Check whether queue is EMPTY. (front == rear)


● Step 2 - If it is EMPTY, then display "Queue is EMPTY!!! Deletion is not
possible!!!" and terminate the function.
● Step 3 - If it is NOT EMPTY, then increment the front value by one (front ++).
Then display queue[front] as deleted element. Then check whether both front and
rear are equal (front == rear), if it TRUE, then set both front and rear to '-1'
(front = rear = -1).

void deQueue(){
if(front == rear)
printf("\nQueue is Empty!!! Deletion is not possible!!!");
else{
printf("\nDeleted : %d", queue[front]);
front++;
if(front == rear)
front = rear = -1;
}}

display() - Displays the elements of a Queue

We can use the following steps to display the elements of a queue...

● Step 1 - Check whether queue is EMPTY. (front == rear)


● Step 2 - If it is EMPTY, then display "Queue is EMPTY!!!" and terminate the
function.
● Step 3 - If it is NOT EMPTY, then define an integer variable 'i' and set 'i = front+1'.
● Step 4 - Display 'queue[i]' value and increment 'i' value by one (i++). Repeat the
same until 'i' value reaches to rear (i <= rear)

void display(){
if(rear == -1)
printf("\nQueue is Empty!!!");
else{
int i;
printf("\nQueue elements are:\n");
for(i=front; i<=rear; i++)
printf("%d\t",queue[i]);
}}

Implementation of Queue Data structure using Array - C Programming


#include<stdio.h>
#include<conio.h>
#define SIZE 10
void enQueue(int);
void deQueue();
void display();
int queue[SIZE], front = -1, rear = -1;
void main()
{
int value, choice;
clrscr();
while(1){
printf("\n\n***** MENU *****\n");
printf("1. Insertion\n2. Deletion\n3. Display\n4. Exit");
printf("\nEnter your choice: ");
scanf("%d",&choice);
switch(choice){
case 1: printf("Enter the value to be insert: ");
scanf("%d",&value);
enQueue(value);
break;
case 2: deQueue();
break;
case 3: display();
break;
case 4: exit(0);
default: printf("\nWrong selection!!! Try again!!!");
}
}
}
void enQueue(int value){
if(rear == SIZE-1)
printf("\nQueue is Full!!! Insertion is not possible!!!");
else{
if(front == -1)
front = 0;
rear++;
queue[rear] = value;
printf("\nInsertion success!!!");
}
}
void deQueue(){
if(front == rear)
printf("\nQueue is Empty!!! Deletion is not possible!!!");
else{
printf("\nDeleted : %d", queue[front]);
front++;
if(front == rear)
front = rear = -1;
}
}
void display(){
if(rear == -1)
printf("\nQueue is Empty!!!");
else{
int i;
printf("\nQueue elements are:\n");
for(i=front; i<=rear; i++)
printf("%d\t",queue[i]);
}
}

LINKED LIST IMPLEMENTATION OF QUEUE


The major problem with the queue implemented using an array is, It will work for

an only fixed number of data values. That means, the amount of data must be specified at

the beginning itself. Queue using an array is not suitable when we don't know the size of
data which we are going to use. A queue data structure can be implemented using a linked

list data structure. The queue which is implemented using a linked list can work for an

unlimited number of values. That means, queue using linked list can work for the variable

size of data (No need to fix the size at the beginning of the implementation). The Queue

implemented using linked list can organize as many data values as we want.

Example

In linked list implementation of a queue, the last inserted node is always pointed by 'rear'
and the first node is always pointed by 'front'.

Operations

To implement queue using linked list, we need to set the following things before
implementing actual operations.

● Step 1 - Include all the header files which are used in the program. And declare all
the user defined functions.
● Step 2 - Define a 'Node' structure with two members data and next.
● Step 3 - Define two Node pointers 'front' and 'rear' and set both to NULL.
● Step 4 - Implement the main method by displaying Menu of list of operations and
make suitable function calls in the main method to perform user selected operation.

enQueue(value) - Inserting an element into the Queue

● Step 1 - Create a newNode with given value and set 'newNode → next' to NULL.
● Step 2 - Check whether queue is Empty (rear == NULL)
● Step 3 - If it is Empty then, set front = newNode and rear = newNode.
● Step 4 - If it is Not Empty then, set rear → next = newNode and rear = newNode.

void Enqueue (int num)


{
struct Node *newNode;
newNode = (struct Node*)malloc(sizeof(struct Node));
newNode->data = value;
newNode -> next = NULL;
if(front == NULL)
front = rear = newNode;
else{
rear -> next = newNode;
rear = newNode;
} printf("\n Insertion is Success!!!\n");
}

deQueue() - Deleting an Element from Queue

● Step 1 - Check whether queue is Empty (front == NULL).


● Step 2 - If it is Empty, then display "Queue is Empty!!! Deletion is not
possible!!!" and terminate from the function
● Step 3 - If it is Not Empty then, define a Node pointer 'temp' and set it to 'front'.
● Step 4 - Then set 'front = front → next' and delete 'temp' (free(temp)).

void dequeue()
{
if(front == NULL)
printf("\nQueue is Empty!!!\n");
else{
struct Node *temp = front;
front = front -> next;
printf("\n Deleted element: %d\n", temp->data);
free(temp);
}
}

display() - Displaying the elements of Queue

● Step 1 - Check whether queue is Empty (front == NULL).


● Step 2 - If it is Empty then, display 'Queue is Empty!!!' and terminate the function.
● Step 3 - If it is Not Empty then, define a Node pointer 'temp' and initialize with
front.
● Step 4 - Display 'temp → data --->' and move it to the next node. Repeat the same
until 'temp' reaches to 'rear' (temp → next != NULL).
● Step 5 - Finally! Display 'temp → data ---> NULL'.

CIRCULAR QUEUE
In a normal Queue Data Structure, elements can be inserted until queue becomes full. But
once the queue becomes full, the next element cannot be inserted until all the elements are
deleted from the queue. For example, consider the queue below...

The queue after inserting all the elements into it is as follows...

Now consider the following situation after deleting three elements from the queue...

This situation also says that Queue is Full and the new element cannot be inserted because
'rear' is still at last position. In the above situation, even though empty positions are
available in the queue it cannot be used to insert the new element. This is the major problem
in a normal queue data structure. To overcome this problem circular queue data structure is
used.

What is Circular Queue?

A circular queue is a linear data structure in which the operations are performed based on
FIFO (First In First Out) principle and the last position is connected back to the first position
to make a circle.

A circular queue solved the limitations of the normal queue. Thus making it a better pick
than the normal queue. It also follows the first come first serve algorithm. Circular Queue is
also called ring Buffer

In Circular Queue, the insertion of a new element is performed at the very first location of
the queue if the last location of the queue is full, in which the first element comes just after
the last element. A circular queue is an abstract data type that contains a collection of data
which allows addition of data at the end of the queue and removal of data at the beginning of
the queue.

· Circular queues have a fixed size.

· Circular queue follows FIFO principle.

Queue items are added at the rear end and the items are deleted at front end of the
circular queue.Here the Queue space is utilized fully by inserting the element at the
Front end if the rear end is full.

Applications of a Circular Queue


Memory management: circular queue is used in memory management.
Process Scheduling: A CPU uses a queue to schedule processes.
Traffic Systems: Queues are also used in traffic systems.

OPERATIONS ON CIRCULAR QUEUE

Fundamental operations performed on the Circular Queue are

· Circular Queue Enqueue

· Circular Queue Dequeue

Formula to be used in Circular Queue

For Enqueue Rear = ( Rear + 1) % ArraySize

For Dequeue Front = ( Front + 1) % ArraySize

Representation of Circular Queue using Arrays

Let’s say the MaxSize of your queue is 5, and the rear pointer has already reached
the end of a queue. There is one empty space at the beginning of a queue, which
means that the front pointer is pointing to location 1.

Rear + 1 = 4 + 1 = 5 (Overflow Error)

Rear = (Rear + 1)% MaxSize = 0 (Reached loc. 0 / Beginning of queue)

Implementation of Circular Queue using an Array

Array implementation starts with the declaration of array and pointer variable

#define MAX_SIZE 5 //global value assignment to max_size variable

int a[MAX_SIZE];
int front = -1;

int rear = -1;

Circular Queue Enqueue Operation

 It is same as Linear Queue EnQueue Operation (i.e) Inserting the element at the Rear
end
 First check for full Queue.
 If the circular queue is full, then insertion is not possible
 Otherwise check for the rear end.
 If the Rear end is full, the elements start getting inserted from the Front end.

Routine to Enqueue an element in circular queue

void Enqueue ( int X, CircularQueue CQ )


{
if( Front = = ( Rear + 1 ) % ArraySize)
Error( “Queue is full!!Insertion not possible” );
else if( Rear = = -1 )
{
Front = Front + 1;
Rear = Rear + 1;
CQ[ Rear ] = X;

else
{
Rear = ( Rear + 1 ) % Arraysize;
CQ[ Rear ] = X;
}
}
Circular Queue DeQueue Operation

 It is same as Linear Queue DeQueue operation (i.e) deleting the front element.
 First check for Empty Queue
 If the Circular Queue is empty, then deletion is not possible
 If the Circular Queue has only one element, then the element is deleted and Front
and Rear pointer is initialized to - 1 to represent Empty Queue
 Otherwise, Front element is deleted and the Front pointer is made to point to next
element in the Circular Queue

Routine To DeQueue An Element In Circular Queue

void DeQueue (CircularQueue CQ)


{
if(Front== - 1)
Empty(“Empty Queue!”);
else if(Front==rear)
{
X=CQ[Front];
Front=-1; Rear=-1;
}
else
{
X=CQ[Front];
Front=(Front+1)%Arraysize;
}}
Implementation of Circular Queue

#include<stdio.h>
# define MAX 3
int cqueue_arr[MAX];
int front = -1;
int rear = -1;
void insert(int item)
{
if((front == 0 && rear == MAX-1) || (front == rear+1))
{
printf("Queue Overflow n");
return;
}
if(front == -1)
{
front = 0;
rear = 0;
}
else
{
if(rear == MAX-1)
rear = 0;
else
rear = rear+1;
}
cqueue_arr[rear] = item ;
}
void deletion()
{
if(front == -1)
{
printf("Queue Underflown");
return ;
}
printf("Element deleted from queue is : %dn",cqueue_arr[front]);
if(front == rear)
{
front = -1;
rear=-1;
}
else
{
if(front == MAX-1)
front = 0;
else
front = front+1;
}
}
void display()
{
int front_pos = front,rear_pos = rear;
if(front == -1)
{
printf("Queue is emptyn");
return;
}
printf("Queue elements :n");
if( front_pos <= rear_pos )
while(front_pos <= rear_pos)
{
printf("%d ",cqueue_arr[front_pos]);
front_pos++ ;
}
else
{
while(front_pos <= MAX-1)
{
printf("%d ",cqueue_arr[front_pos]);
front_pos++ ;
}
front_pos = 0;
while(front_pos <= rear_pos)
{
printf("%d ",cqueue_arr[front_pos]);
front_pos++;
}
}
printf("n");
}
int main()
{
int choice,item;
do
{
printf("\n 1.Insertn");
printf("\n 2.Deleten");
printf("\n 3.Displayn");
printf("\n 4.Quitn");
printf("\n Enter your choice : ");
scanf("%d",&choice);
switch(choice)
{
case 1 :
printf("\n Input the element for insertion in queue : ");
scanf("%d", &item);
insert(item);
break;
case 2 :
deletion();
break;
case 3:
display();
break;
case 4:
break;
default:
printf("\n Wrong choicen");
}
}while(choice!=4);
return 0;
}

OUTPUT
1.Insertn
2.Deleten
3.Displayn
4.Quitn
Enter your choice : 1
Input the element for insertion in queue : 100
1.Insertn
2.Deleten
3.Displayn
4.Quitn
Enter your choice : 1
Input the element for insertion in queue : 200
1.Insertn
2.Deleten
3.Displayn
4.Quitn
Enter your choice : 2
Element deleted from queue is : 100n
1.Insertn
2.Deleten
3.Displayn
4.Quitn
Enter your choice : 3
Queue elements :n200 n
1.Insertn
2.Deleten
3.Displayn
4.Quitn

DEQUEUE
DOUBLE ENDED QUEUE
Double ended queue is a more generalized form of queue data structure which allows
insertion and removal of elements from both the ends, i.e , front and back. Double Ended
Queue also called as deque (pronounced as ‘deck’ or ‘dequeue’) is a list in which the
elements can be inserted or deleted at either end in constant time. It is also known as a
head-tail linked list because elements can be added to or removed from either the front
(head) or the back (tail) end. However, no element can be added and deleted from the
middle. In a sense, this hybrid linear structure provides all the capabilities of stacks and
queues in a single data structure.

There are two variants of a double-ended queue. They include:


· Input restricted deque: In this dequeue, insertions can be done only at one of the ends, while
deletions can be done from both ends.

Output restricted deque: In this dequeue,deletions can be done only at one of the ends,while
insertions can be done on both ends.
There are four basic operations in usage of Deque
1. Insertion at rear end - Enqueue Right
2. Insertion at front end - Enqueue
Left
3. Deletion at front end - Dequeue Left
4. Deletion at rear end - Dequeue Right
We can perform two more operations on dequeue:
o isFull(): This function returns a true value if the stack is full; otherwise, it returns a
false value.
o isEmpty(): This function returns a true value if the stack is empty; otherwise it
returns a false value.
➔ For input restricted dequeue the operations are Enqueue Right, Dequeue Left, Dequeue
Right.
➔ For output restricted dequeue the operations are Enqueue Left, Enqueue Right, Dequeue
Left.
Memory Representation
The deque can be implemented using two data structures, i.e., circular array, and doubly
linked list.

The Deque Abstract Data Type


The deque abstract data type is defined by the following structure and operations. A deque is
structured, as described above, as an ordered collection of items where items are added and
removed from either end, either front or rear. The deque operations are given below.
 Deque() creates a new deque that is empty. It needs no parameters and returns an
empty deque.
 add_front(item) adds a new item to the front of the deque. It needs the item and
returns nothing.
 add_rear(item) adds a new item to the rear of the deque. It needs the item and returns
nothing.
 remove_front() removes the front item from the deque. It needs no parameters and
returns the item. The deque is modified.
 remove_rear() removes the rear item from the deque. It needs no parameters and
returns the item. The deque is modified.
 is_empty() tests to see whether the deque is empty. It needs no parameters and
returns a boolean value.
 size() returns the number of items in the deque. It needs no parameters and returns an
integer.
As an example, if we assume that d is a deque that has been created and is currently empty,
the table below shows the results of a sequence of deque operations.
Deque Operation Deque Contents Return Value
d.is_empty() [] True
d.add_rear(4) [4]
d.add_rear('dog') ['dog', 4]
d.add_front('cat') ['dog', 4, 'cat']
d.add_front(True) ['dog', 4,'cat', True]
d.add_front(True) ['dog', 4,'cat', True]
d.size() ['dog', 4, 'cat', True] 4
d.is_empty() ['dog', 4, 'cat', True] False
d.add_rear(8.4) [8.4, 'dog', 4, 'cat',True]

IMPLEMENTATION OF DEQUE
Time Complexity: Time complexity of all operations like insertfront(), insertlast(),
deletefront(), deletelast()is O(1).

// Maximum size of array or Dequeue


#define SIZE 5
class Dequeue
{
//front and rear to store the head and tail pointers
int *arr;
int front, rear;
public :
Dequeue()
{
//Create the array
arr = new int[SIZE];
//Initialize front and rear with -1
front = -1;
rear = -1;
}
// Operations on Deque
void push_front(int );
void push_back(int );
void pop_front();
void pop_back();
int get_front();
int get_back();
bool full();
bool empty();
};
Insert Elements at Front
First we check if the queue is full. If its not full we insert an element at front end by
following the given conditions :
 If the queue is empty then intialize front and rear to 0. Both will point to the first
element.

 Else we decrement front and insert the element. Since we are using circular array, we
have to keep in mind that if front is equal to 0 then instead of decreasing it by 1 we
make it equal to SIZE-1.
void Dequeue :: push_front(int key)
{
if(full())
{
cout << "OVERFLOW\n";
}
else
{
//If queue is empty
if(front == -1)
front = rear = 0;

//If front points to the first position element


else if(front == 0)
front = SIZE-1;

else
--front;

arr[front] = key;
}
}
Insert Elements at back
Again we check if the queue is full. If its not full we insert an element at back by following
the given conditions:
 If the queue is empty then intialize front and rear to 0. Both will point to the first
element.
 Else we increment rear and insert the element. Since we are using circular array, we
have to keep in mind that if rear is equal to SIZE-1 then instead of increasing it by 1
we make it equal to 0

void Dequeue :: push_back(int key)


{
if(full())
{
cout << "OVERFLOW\n";
}
else
{
//If queue is empty
if(front == -1)
front = rear = 0;

//If rear points to the last element


else if(rear == SIZE-1)
rear = 0;

else
++rear;

arr[rear] = key;
}
}
Delete First Element
In order to do this, we first check if the queue is empty. If its not then delete the front
element by following the given conditions :
 If only one element is present we once again make front and rear equal to -1.
 Else we increment front. But we have to keep in mind that if front is equal to SIZE-1
then instead of increasing it by 1 we make it equal to 0.

void Dequeue :: pop_front()


{
if(empty())
{
cout << "UNDERFLOW\n";
}
else
{
//If only one element is present
if(front == rear)
front = rear = -1;

//If front points to the last element


else if(front == SIZE-1)
front = 0;

else
++front;
}
}
Delete Last Element
Inorder to do this, we again first check if the queue is empty. If its not then we delete the last
element by following the given conditions :
 If only one element is present we make front and rear equal to -1.
 Else we decrement rear. But we have to keep in mind that if rear is equal to 0 then
instead of decreasing it by 1 we make it equal to SIZE-1.

void Dequeue :: pop_back()


{
if(empty())
{
cout << "UNDERFLOW\n";
}
else
{
//If only one element is present
if(front == rear)
front = rear = -1;

//If rear points to the first position element


else if(rear == 0)
rear = SIZE-1;

else
--rear;
}
}
Check if Queue is empty
It can be simply checked by looking where front points to. If front is still intialized with -1,
the queue is empty.
bool Dequeue :: empty()
{
if(front == -1)
return true;
else
return false;
}
Check if Queue is full
Since we are using circular array, we check for following conditions as shown in code to
check if queue is full.
bool Dequeue :: full()
{
if((front == 0 && rear == SIZE-1) ||
(front == rear + 1))
return true;
else
return false;
}
Return First Element
If the queue is not empty then we simply return the value stored in the position which front
points.
int Dequeue :: get_front()
{
if(empty())
{ cout << "f=" <<front << endl;
cout << "UNDERFLOW\n";
return -1;
}
else
{
return arr[front];
}
}
Return Last Element
If the queue is not empty then we simply return the value stored in the position which rear
points.
int Dequeue :: get_back()
{
if(empty())
{
cout << "UNDERFLOW\n";
return -1;
}
else
{
return arr[rear];
}
}.

Implementation of Deque in C
#include<stdio.h>
# define MAX 5
int deque_arr[MAX];
int left = -1;
int right = -1;
/*Begin of insert_right*/
void insert_right()
{
int added_item;
if((left == 0 && right == MAX-1) || (left == right+1))
{ printf("Queue Overflow\n");
return;}
if (left == -1) /* if queue is initially empty */
{ left = 0;
right = 0;}
else
if(right == MAX-1) /*right is at last position of queue */
right = 0;
else
right = right+1;
printf("Input the element for adding in queue : ");
scanf("%d", &added_item);
deque_arr[right] = added_item ;
}
/*End of insert_right*/

/*Begin of insert_left*/
void insert_left()
{ int added_item;
if((left == 0 && right == MAX-1) || (left == right+1))
{ printf("Queue Overflow \n");
return; }
if (left == -1)/*If queue is initially empty*/
{ left = 0;
right = 0; }
else
if(left== 0)
left=MAX-1;
else
left=left-1;
printf("Input the element for adding in queue : ");
scanf("%d", &added_item);
deque_arr[left] = added_item ; }
/*End of insert_left*/

/*Begin of delete_left*/
void delete_left()
{ if (left == -1)
{ printf("Queue Underflow\n");
return ; }
printf("Element deleted from queue is : %d\n",deque_arr[left]);
if(left == right) /*Queue has only one element */
{ left = -1;
right=-1; }
else
if(left == MAX-1)
left = 0;
else
left = left+1;
}
/*End of delete_left*/

/*Begin of delete_right*/
void delete_right()
{if (left == -1)
{printf("Queue Underflow\n");
return ; }
printf("Element deleted from queue is :
%d\n",deque_arr[right]);
if(left == right) /*queue has only one element*/
{ left = -1;
right=-1; }
else
if(right == 0)
right=MAX-1;
else
right=right-1; }
/*End of delete_right*/
/*Begin of input_que*/
void display_queue()
{ int front_pos = left,rear_pos = right;
if(left == -1)
{ printf("Queue is empty\n");
return; }
printf("Queue elements :\n");
if( front_pos <= rear_pos )
{ while(front_pos <= rear_pos)
{ printf("%d ",deque_arr[front_pos]);
front_pos++; } }
else
{ while(front_pos <= MAX-1)
{ printf("%d ",deque_arr[front_pos]);
front_pos++; }
front_pos = 0;
while(front_pos <= rear_pos)
{ printf("%d ",deque_arr[front_pos]);
front_pos++;
}
}
printf("\n");
}
/*End of display_queue*/
/*Begin of input_que*/
void input_que()
{ int choice;
do
{ printf("1.Insert at right\n");
printf("2.Delete from left\n");
printf("3.Delete from right\n");
printf("4.Display\n");
printf("5.Quit\n");
printf("Enter your choice : ");
scanf("%d",&choice);

switch(choice)
{ case 1:
insert_right();
break;
case 2:
delete_left();
break;
case 3:
delete_right();
break;
case 4:
display_queue();
break;
case 5:
break;
default:
printf("Wrong choice\n");
}
}while(choice!=5);
}
/*End of input_que*/

/*Begin of output_que*/
void output_que()
{ int choice;
do
{ printf("1.Insert at right\n");
printf("2.Insert at left\n");
printf("3.Delete from left\n");
printf("4.Display\n");
printf("5.Quit\n");
printf("Enter your choice : ");
scanf("%d",&choice);
switch(choice)
{
case 1:
insert_right();
break;
case 2:
insert_left();
break;
case 3:
delete_left();
break;
case 4:
display_queue();
break;
case 5:
break;
default:
printf("Wrong choice\n");
}
}while(choice!=5);
}
/*End of output_que*/

/*Begin of main*/
main()
{ int choice;
printf("1.Input restricted dequeue\n");
printf("2.Output restricted dequeue\n");
printf("Enter your choice : ");
scanf("%d",&choice);
switch(choice)
{
case 1 :
input_que();
break;
case 2:
output_que();
break;
default:
printf("Wrong choice\n");
}
}
/*End of main*/

OUPUT
1.Input restricted dequeue
2.Output restricted dequeue
Enter your choice : 2
1.Insert at right
2.Insert at left
3.Delete from left
4.Display
5.Quit
Enter your choice : 2
Input the element for adding in queue : 10
1.Insert at right
2.Insert at left
3.Delete from left
4.Display
5.Quit
Enter your choice : 1
Input the element for adding in queue : 20
1.Insert at right
2.Insert at left
3.Delete from left
4.Display
5.Quit
Enter your choice : 4
Queue elements :
10 20
1.Insert at right
2.Insert at left
3.Delete from left
4.Display
5.Quit
Enter your choice : 3
Element deleted from queue is : 10
1.Insert at right
2.Insert at left
3.Delete from left
4.Display
5.Quit
Enter your choice : 4
Queue elements :
20
1.Insert at right
2.Insert at left
3.Delete from left
4.Display
5.Quit
Enter your choice :

APPLICATIONS OF DEQUE
o The deque can be used as a stack and queue; therefore, it can perform both redo and
undo operations.
o Deque data structure supports clockwise and anticlockwise rotations in O(1) time
which can be useful in certain applications.
o Also, the problems where elements need to be removed and or added both ends can
be efficiently solved using Deque.
o An internet browser’s history. Recently visited URLs are supplementary to the front
of the deque, and also the uniform resource locator at the rear of the deque is
removed when some such range of insertions at the front.

o It can be used as a palindrome checker means that if we read the string from both
ends, then the string would be the same.
o It can be used for multiprocessor scheduling. Suppose we have two processors, and
each processor has one process to execute. Each processor is assigned with a process
or a job, and each process contains multiple threads. Each processor maintains a
deque that contains threads that are ready to execute. The processor executes a
process, and if a process creates a child process then that process will be inserted at
the front of the deque of the parent process. Suppose the processor P2 has completed
the execution of all its threads then it steals the thread from the rear end of the
processor P1 and adds to the front end of the processor P2. The processor P2 will take
the thread from the front end; therefore, the deletion takes from both the ends, i.e.,
front and rear end. This is known as the A-steal algorithm for scheduling.
Applications Of Queues Palindrome Checker

A palindrome is a word or sequence which reads the same from any direction i.e. either
backward or forward.

•Reviver, Radar, Level are few examples of palindrome.

Steal Job Scheduling algorithm

This algorithm is used for implementing the task scheduling for multiple processors often
called multiprocessor scheduling. The processor takes the first element from the deque. A thread
can be accessed from another processor, when one of the processors completes execution of its
own threads. The last element from the deque of another processor is accessed by one of the
processors which then executes it. Undo Redo operations The Deque can be implemented to
achieve the functionality of Undo Redo operations in software applications.

General applications

There are several algorithms that use queues to give efficient running times. When jobs are
submitted to a printer, they are arranged in order of arrival. Thus, essentially, jobs sent to a line
printer are placed on a queue. We say essentially a queue, because jobs can be killed. This
amounts to a deletion from the middle of the queue, which is a violation of the strict definition.
Virtually every real-life line is (supposed to be) a queue. For instance, lines at ticket counters are
queues, because service is first-come first-served. Another example concerns computer networks.
There are many network setups of personal computers in which the disk is attached to one
machine, known as the file server. Users on other machines are given access to files on a first-
come first-served basis, so the data structure is a queue.

Calls to large companies are generally placed on a queue when all operators are busy. In large
universities, where resources are limited, students must sign a waiting list if all terminals are
occupied. The student who has been at a terminal the longest is forced off first, and the student
who has been waiting the longest is the next user to be allowed on.

A whole branch of mathematics, known as queueing theory, deals with computing,


probabilistically, how long users expect to wait on a line, how long the line gets, and other such
questions. The answer depends on how frequently users arrive to the line and how long it takes to
process a user once the user is served. Both of these parameters are given as probability
distribution functions. In simple cases, an answer can be computed analytically.

An example of an easy case would be a phone line with one operator. If the operator is busy,
callers are placed on a waiting line (up to some maximum limit). This problem is important for
businesses, because studies have shown that people are quick to hang up the phone.

CPU scheduling in multiprogramming environment

In a multiprogramming environment, a single CPU has to serve more than one program
simultaneously. Consider a multiprogramming environment where possible jobs to the CPU are
categorized into three group:

1. Interrupts to be serviced. Variety of devices and terminals are connected with the CPU
and they may interrupt at any moment to get a service from it.

2. Interactive users to be serviced. These are mainly student's programs in various


terminals under execution

3. Batch jobs to be serviced. These are long term jobs mainly from the non-interactive
users, where all the inputs are fed when jobs are submitted; simulation programs, and jobs to
print documents are of this kind. Here the problem is to schedule all sorts of jobs so that the
required level of performance of the environment will be attained.

One way to implement complex scheduling is to classify the workload according to its
characteristics and to maintain separate process queues. So far the environment is concerned,
This approach is often called multi-level queues scheduling. Process will be assigned to their
respective queues. CPU will then service the processes as per the priority of the queues. In case
of a simple strategy, absolute priority, the process from the highest priority queue (for example,
system processes) are serviced until the queue becomes empty. Then the CPU switches to the
queue of interactive processes which are having medium-priority and so on. A lower- priority
process may, of course. be pre-empted by a higher-priority arrival in one of the upper- level
queues. Multi-level queues strategy is a general discipline but has some drawbacks.

The main drawback is that when the process arrives in higher-priority queues is very high.
The processes in the lower-priority queue may starve for a long time. One way out to solve this
problem is to time slice between the queues. Bach queue gets a certain portion of the CPU time.

Another possibility is known as multi-level feedback queue strategy. Normally in


multilevel queue strategy, processes are permanently assigned to a queue upon entry to the
system and processes do not move among queues. Multi-level feedback queue strategy, on the
contrary, allows a process to move between queues. The idea is to separate out processes with
different CPU burst characteristics. If a process uses too much CPU time (that is a long run
process), it will be moved to a lower priority queue.

Similarly. a process which waits too long in a lower-priority queue may be moved to a
higher-priority queue. For example, consider a multi-level feedback queue strategy with three
queues Q1,Q2 and Q3. A process entering the system is put in queue Q1. A process in Q1 is
given a time quantum τ of 10 ms, say, it does not finish within this time, it is moved to the tail of
queue Q2.

If Q1 is empty, the process at the front of queue Q2 is given a time quantum τ of 20 ms,
say. If it does not complete within this time quantum. it is pre-empted and put into queue Q3.
Processes in queue Q3 are serviced only when queues Q1 and Q2 are empty.

Thus, with this strategy, CPU first executes all processes in queue Q1. Only when Q1 is
empty it will execute all processes in queue Q2. Similarly, processes in queue Q3 will only be
executed if queues Q1 and Q2 are empty. A process which arrives in queue Q1 will pre-empt a
process in queue Q2 or Q3. lt can be observed that, this strategy gives the highest priority to any
process with a CPU burst of 10 ms or less. Processes which need more than 10 ms. but less than
or equal to 20 ms are also served quickly, that is, it gets the next highest priority than the shorter
processes.

Longer processes automatically sink to queue Q3, from Q3. Processes will be served in
First Come First-Serve (FCFS) basis and in case of process waiting for a too long time (as
decided by the scheduler) may be put into the tail of queue Q1.

You might also like