0% found this document useful (0 votes)
10 views

Unit 4

DSU notes

Uploaded by

ketankotane70
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)
10 views

Unit 4

DSU notes

Uploaded by

ketankotane70
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/ 48

Introduction to Stack

What is a Stack?
A Stack is a linear data structure that follows the LIFO (Last-In-First-Out) principle. Stack has
one end, whereas the Queue has two ends (front and rear). It contains only one pointer top
pointer pointing to the topmost element of the stack. Whenever an element is added in the stack,
it is added on the top of the stack, and the element can be deleted only from the stack. In other
words, a stack can be defined as a container in which insertion and deletion can be done from
the one end known as the top of the stack.

Some key points related to stack

 It is called as stack because it behaves like a real-world stack, piles of books, etc.
 A Stack is an abstract data type with a pre-defined capacity, which means that it can store
the elements of a limited size.
 It is a data structure that follows some order to insert and delete the elements, and that
order can be LIFO or FILO.

Working of Stack

Stack works on the LIFO pattern. As we can observe in the below figure there are five memory
blocks in the stack; therefore, the size of the stack is 5.

Suppose we want to store the elements in a stack and let's assume that stack is empty. We have
taken the stack of size 5 as shown below in which we are pushing the elements one by one until
the stack becomes full.

Since our stack is full as the size of the stack is 5. In the above cases, we can observe that it goes
from the top to the bottom when we were entering the new element in the stack. The stack gets
filled up from the bottom to the top.
When we perform the delete operation on the stack, there is only one way for entry and exit as
the other end is closed. It follows the LIFO pattern, which means that the value entered first will
be removed last. In the above case, the value 5 is entered first, so it will be removed only after
the deletion of all the other elements.

Standard Stack Operations

The following are some common operations implemented on the stack:

 push(): When we insert an element in a stack then the operation is known as a push. If
the stack is full then the overflow condition occurs.
 pop(): When we delete an element from the stack, the operation is known as a pop. If the
stack is empty means that no element exists in the stack, this state is known as an
underflow state.
 isEmpty(): It determines whether the stack is empty or not.
 isFull(): It determines whether the stack is full or not.'
 peek(): It returns the element at the given position.
 count(): It returns the total number of elements available in a stack.
 change(): It changes the element at the given position.
 display(): It prints all the elements available in the stack.

PUSH operation

The steps involved in the PUSH operation is given below:

 Before inserting an element in a stack, we check whether the stack is full.


 If we try to insert the element in a stack, and the stack is full, then the overflow condition
occurs.
 When we initialize a stack, we set the value of top as -1 to check that the stack is empty.
 When the new element is pushed in a stack, first, the value of the top gets incremented,
i.e., top=top+1, and the element will be placed at the new position of the top.
 The elements will be inserted until we reach the max size of the stack.
POP operation

The steps involved in the POP operation is given below:

 Before deleting the element from the stack, we check whether the stack is empty.
 If we try to delete the element from the empty stack, then the underflow condition
occurs.
 If the stack is not empty, we first access the element which is pointed by the top
 Once the pop operation is performed, the top is decremented by 1, i.e., top=top-1.

Applications of Stack

The following are the applications of the stack:

1. Balancing of symbols: Stack is used for balancing a symbol. For example, we have the
following program
2. int main()
3. {
4. cout<<"Hello";
5.
6. }

As we know, each program has an opening and closing braces; when the opening braces come,
we push the braces in a stack, and when the closing braces appear, we pop the opening braces
from the stack. Therefore, the net value comes out to be zero. If any symbol is left in the stack, it
means that some syntax occurs in a program.

 String reversal: Stack is also used for reversing a string. For example, we want to
reverse a "hello" string, so we can achieve this with the help of a stack.
First, we push all the characters of the string in a stack until we reach the null character.
After pushing all the characters, we start taking out the character one by one until we
reach the bottom of the stack.
 UNDO/REDO: It can also be used for performing UNDO/REDO operations. For
example, we have an editor in which we write 'a', then 'b', and then 'c'; therefore, the text
written in an editor is abc. So, there are three states, a, ab, and abc, which are stored in a
stack. There would be two stacks in which one stack shows UNDO state, and the other
shows REDO state.
If we want to perform UNDO operation, and want to achieve 'ab' state, then we
implement pop operation.
 Recursion: The recursion means that the function is calling itself again. To maintain the
previous states, the compiler creates a system stack in which all the previous records of
the function are maintained.
 DFS(Depth First Search): This search is implemented on a Graph, and Graph uses the
stack data structure.
 Backtracking: Suppose we have to create a path to solve a maze problem. If we are
moving in a particular path, and we realize that we come on the wrong way. In order to
come at the beginning of the path to create a new path, we have to use the stack data
structure.
 Expression conversion: Stack can also be used for expression conversion. This is one of
the most important applications of stack. The list of the expression conversion is given
below:
 Infix to prefix
 Infix to postfix
 Prefix to infix
 Prefix to postfix
Postfix to infix

 Memory management: The stack manages the memory. The memory is assigned in the
contiguous memory blocks. The memory is known as stack memory as all the variables
are assigned in a function call stack memory. The memory size assigned to the program is
known to the compiler. When the function is created, all its variables are assigned in the
stack memory. When the function completed its execution, all the variables assigned in
the stack are released.
Array implementation of Stack
In array implementation, the stack is formed by using the array. All the operations regarding the
stack are performed using arrays. Lets see how each operation can be implemented on the stack
using array data structure.

Adding an element onto the stack (push operation)

Adding an element into the top of the stack is referred to as push operation. Push operation
involves following two steps.

1. Increment the variable Top so that it can now refere to the next memory location.
2. Add element at the position of incremented top. This is referred to as adding new element
at the top of the stack.

Stack is overflown when we try to insert an element into a completely filled stack therefore, our
main function must always avoid stack overflow condition.

Algorithm:

1. begin
2. if top = n then stack full
3. top = top + 1
4. stack (top) : = item;
5. end

Time Complexity : o(1)

implementation of push algorithm in C language

1. void push (int val,int n) //n is size of the stack


2. {
3. if (top == n )
4. printf("\n Overflow");
5. else
6. {
7. top = top +1;
8. stack[top] = val;
9. }
10. }

Deletion of an element from a stack (Pop operation)

Deletion of an element from the top of the stack is called pop operation. The value of the
variable top will be incremented by 1 whenever an item is deleted from the stack. The top most
element of the stack is stored in an another variable and then the top is decremented by 1. the
operation returns the deleted value that was stored in another variable as the result.

The underflow condition occurs when we try to delete an element from an already empty stack.

Algorithm :

1. begin
2. if top = 0 then stack empty;
3. item := stack(top);
4. top = top - 1;
5. end;

Time Complexity : o(1)

Implementation of POP algorithm using C language

1. int pop ()
2. {
3. if(top == -1)
4. {
5. printf("Underflow");
6. return 0;
7. }
8. else
9. {
10. return stack[top - - ];
11. }
12. }

Visiting each element of the stack (Peek operation)

Peek operation involves returning the element which is present at the top of the stack without
deleting it. Underflow condition can occur if we try to return the top element in an already empty
stack.

Algorithm :

PEEK (STACK, TOP)

1. Begin
2. if top = -1 then stack empty
3. item = stack[top]
4. return item
5. End
Time complexity: o(n)

Implementation of Peek algorithm in C language

1. int peek()
2. {
3. if (top == -1)
4. {
5. printf("Underflow");
6. return 0;
7. }
8. else
9. {
10. return stack [top];
11. }
12. }

C program

#include <stdio.h>

int stack[100],i,j,choice=0,n,top=-1;

void push();

void pop();

void show();

void main ()

printf("Enter the number of elements in the stack ");

scanf("%d",&n);

printf("*********Stack operations using array*********");

printf("\n----------------------------------------------\n");

while(choice != 4)
{

printf("Chose one from the below options...\n");

printf("\n1.Push\n2.Pop\n3.Show\n4.Exit");

printf("\n Enter your choice \n");

scanf("%d",&choice);

switch(choice)

case 1:

push();

break;

case 2:

pop();

break;

case 3:

show();

break;

case 4:
{

printf("Exiting....");

break;

default:

printf("Please Enter valid choice ");

};

void push ()

int val;

if (top == n )

printf("\n Overflow");

else

printf("Enter the value?");

scanf("%d",&val);

top = top +1;

stack[top] = val;
}

void pop ()

if(top == -1)

printf("Underflow");

else

top = top -1;

void show()

for (i=top;i>=0;i--)

printf("%d\n",stack[i]);

if(top == -1)

printf("Stack is empty");

}
Convert Infix to Postfix notation
Before understanding the conversion from infix to postfix notation, we should know about the
infix and postfix notations separately.

An infix and postfix are the expressions. An expression consists of constants, variables, and
symbols. Symbols can be operators or parenthesis. All these components must be arranged
according to a set of rules so that all these expressions can be evaluated using the set of rules.

Examples of expressions are:

5+6

A-B

(P * 5)

All the above expressions have a common structure, i.e., we have an operator between the two
operands. An Operand is an object or a value on which the operation is to be performed. In the
above expressions, 5, 6 are the operands while '+', '-', and '*' are the operators.

What is infix notation?

When the operator is written in between the operands, then it is known as infix notation.
Operand does not have to be always a constant or a variable; it can also be an expression itself.

For example,

(p + q) * (r + s)

In the above expression, both the expressions of the multiplication operator are the operands, i.e.,
(p + q), and (r + s) are the operands.

In the above expression, there are three operators. The operands for the first plus operator are p
and q, the operands for the second plus operator are r and s. While performing the operations on
the expression, we need to follow some set of rules to evaluate the result. In the above
expression, addition operation would be performed on the two expressions, i.e., p+q and r+s, and
then the multiplication operation would be performed.

Syntax of infix notation is given below:

<operand> <operator> <operand>


If there is only one operator in the expression, we do not require applying any rule. For example,
5 + 2; in this expression, addition operation can be performed between the two operands (5 and
2), and the result of the operation would be 7.

If there are multiple operators in the expression, then some rule needs to be followed to evaluate
the expression.

If the expression is:

4+6*2

If the plus operator is evaluated first, then the expression would look like:

10 * 2 = 20

If the multiplication operator is evaluated first, then the expression would look like:

4 + 12 = 16

The above problem can be resolved by following the operator precedence rules. In the algebraic
expression, the order of the operator precedence is given in the below table:

Precedence order

Operators Symbols

Parenthesis { }, ( ), [ ]

Exponential notation ^

Multiplication and Division *, /

Addition and Subtraction +, -

Associativity means when the operators with the same precedence exist in the expression. For
example, in the expression, i.e., A + B - C, '+' and '-' operators are having the same precedence,
so they are evaluated with the help of associativity. Since both '+' and '-' are left-associative, they
would be evaluated as (A + B) - C.

Associativity order

Operators Associativity

^ Right to Left
*, / Left to Right

+, - Left to Right

Let's understand the associativity through an example.

1 + 2*3 + 30/5

Since in the above expression, * and / have the same precedence, so we will apply the
associativity rule. As we can observe in the above table that * and / operators have the left to
right associativity, so we will scan from the leftmost operator. The operator that comes first will
be evaluated first. The operator * appears before the / operator, and multiplication would be done
first.

1+ (2*3) + (30/5)

1+6+6 = 13

For example:

2^2^3 = 2 ^ 8

= 256

After exponent, multiplication, and division operators are evaluated. If both the operators are
present in the expression, then the operation will be applied from left to right.

The next preference is given to addition and subtraction. If both the operators are available in the
expression, then we go from left to right.

The operators that have the same precedence termed as operator associativity. If we go from
left to right, then it is known as left-associative. If we go from right to left, then it is known as
right-associative.

Problem with infix notation

To evaluate the infix expression, we should know about the operator precedence rules, and if
the operators have the same precedence, then we should follow the associativity rules. The use
of parenthesis is very important in infix notation to control the order in which the operation to be
performed. Parenthesis improves the readability of the expression. An infix expression is the
most common way of writing expression, but it is not easy to parse and evaluate the infix
expression without ambiguity. So, mathematicians and logicians studied this problem and
discovered two other ways of writing expressions which are prefix and postfix. Both expressions
do not require any parenthesis and can be parsed without ambiguity. It does not require operator
precedence and associativity rules.
Postfix Expression

The postfix expression is an expression in which the operator is written after the operands. For
example, the postfix expression of infix notation ( 2+3) can be written as 23+.

Some key points regarding the postfix expression are:

 In postfix expression, operations are performed in the order in which they have written from left
to right.
 It does not any require any parenthesis.
 We do not need to apply operator precedence rules and associativity rules.

Algorithm to evaluate the postfix expression

 Scan the expression from left to right until we encounter any operator.
 Perform the operation
 Replace the expression with its computed value.
 Repeat the steps from 1 to 3 until no more operators exist.

Let's understand the above algorithm through an example.

Infix expression: 2 + 3 * 4

We will start scanning from the left most of the expression. The multiplication operator is an
operator that appears first while scanning from left to right. Now, the expression would be:

Expression = 2 + 34*

= 2 + 12

Again, we will scan from left to right, and the expression would be:

Expression = 2 12 +

= 14

Evaluation of postfix expression using stack.

 Scan the expression from left to right.


 If we encounter any operand in the expression, then we push the operand in the stack.
 When we encounter any operator in the expression, then we pop the corresponding operands
from the stack.
 When we finish with the scanning of the expression, the final value remains in the stack.

Let's understand the evaluation of postfix expression using stack.


Example 1: Postfix expression: 2 3 4 * +

Input Stack

2 3 4 * + empty Push 2

34*+ 2 Push 3

4*+ 32 Push 4

*+ 432 Pop 4 and 3, and perform 4*3 = 12. Push 12 into the stack.

+ 12 2 Pop 12 and 2 from the stack, and perform 12+2 = 14. Push 14 into the stack.

The result of the above expression is 14.

Example 2: Postfix expression: 3 4 * 2 5 * +

Input Stack

3 4 * 2 5 * + empty Push 3

4*25*+ 3 Push 4

*2 5 * + 43 Pop 3 and 4 from the stack and perform 3*4 = 12. Push 12 into the stack.

25*+ 12 Push 2

5*+ 2 12 Push 5

*+ 5 2 12 Pop 5 and 2 from the stack and perform 5*2 = 10. Push 10 into the stack.

+ 10 12 Pop 10 and 12 from the stack and perform 10+12 = 22. Push 22 into the stack.

The result of the above expression is 22.

Algorithm to evaluate postfix expression

1. Read a character
2. If the character is a digit, convert the character into int and push the integer into the stack.
3. If the character is an operator,
o Pop the elements from the stack twice obtaining two operands.
o Perform the operation
o Push the result into the stack.
Conversion of infix to postfix

Here, we will use the stack data structure for the conversion of infix expression to prefix
expression. Whenever an operator will encounter, we push operator into the stack. If we
encounter an operand, then we append the operand to the expression.

Rules for the conversion from infix to postfix expression

1. Print the operand as they arrive.


2. If the stack is empty or contains a left parenthesis on top, push the incoming operator on to the
stack.
3. If the incoming symbol is '(', push it on to the stack.
4. If the incoming symbol is ')', pop the stack and print the operators until the left parenthesis is
found.
5. If the incoming symbol has higher precedence than the top of the stack, push it on the stack.
6. If the incoming symbol has lower precedence than the top of the stack, pop and print the top of
the stack. Then test the incoming operator against the new top of the stack.
7. If the incoming operator has the same precedence with the top of the stack then use the
associativity rules. If the associativity is from left to right then pop and print the top of the stack
then push the incoming operator. If the associativity is from right to left then push the incoming
operator.
8. At the end of the expression, pop and print all the operators of the stack.

Let's understand through an example.

Infix expression: K + L - M*N + (O^P) * W/U/V * T + Q

Input Expression Stack Postfix Expression

K K

+ +

L + KL

- - K L+

M - K L+ M

* -* K L+ M

N -* KL+MN

K L + M N*
+ +
K L + M N* -
( +( K L + M 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*
+ +
KL+MN*-OP^W*U/V/T*+

Q + KL+MN*-OP^W*U/V/T*Q

KL+MN*-OP^W*U/V/T*+Q+

The final postfix expression of infix expression(K + L - M*N + (O^P) * W/U/V * T + Q) is


KL+MN*-OP^W*U/V/T*+Q+.

Evaluation of Postfix Expression


Example:

Input: str = “2 3 1 * + 9 -“
Output: -4
Explanation:

 Scan 2, it’s a number, so push it to stack. Stack contains ‘2’


 Scan 3, again a number, push it to stack, stack now contains ‘2 3’ (from bottom to top)
 Scan 1, again a number, push it to stack, stack now contains ‘2 3 1’
 Scan *, it’s an operator, pop two operands from stack, apply the * operator on operands, we get
3*1 which results in 3. We push the result 3 to stack. The stack now becomes ‘2 3’.
 Scan +, it’s an operator, pop two operands from stack, apply the + operator on operands, we get
3 + 2 which results in 5. We push the result 5 to stack. The stack now becomes ‘5’.
 Scan 9, it’s a number, so we push it to the stack. The stack now becomes ‘5 9’.
 Scan -, it’s an operator, pop two operands from stack, apply the – operator on operands, we get
5 – 9 which results in -4. We push the result -4 to the stack. The stack now becomes ‘-4’.
 There are no more elements to scan, we return the top element from the stack (which is the
only element left in a stack).

Input: str = “100 200 + 2 / 5 * 7 +”


Output: 757

Convert infix to prefix notation


What is Prefix notation?

A prefix notation is another form of expression but it does not require other information such as
precedence and associativity, whereas an infix notation requires information of precedence and
associativity. It is also known as polish notation. In prefix notation, an operator comes before
the operands. The syntax of prefix notation is given below:

<operator> <operand> <operand>

For example, if the infix expression is 5+1, then the prefix expression corresponding to this
infix expression is +51.

If the infix expression is:

a*b+c

*ab+c

+*abc (Prefix expression)


Consider another example:

A+B*C

First scan: In the above expression, multiplication operator has a higher precedence than the
addition operator; the prefix notation of B*C would be (*BC).

A + *BC

Second scan: In the second scan, the prefix would be:

+A *BC

In the above expression, we use two scans to convert infix to prefix expression. If the expression
is complex, then we require a greater number of scans. We need to use that method that requires
only one scan, and provides the desired result. If we achieve the desired output through one scan,
then the algorithm would be efficient. This is possible only by using a stack.

Conversion of Infix to Prefix using Stack

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

If we are converting the expression from infix to prefix, we need first to reverse the expression.

The Reverse expression would be:

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

To obtain the prefix expression, we have created a table that consists of three columns, i.e., input
expression, stack, and prefix expression. When we encounter any symbol, we simply add it into
the prefix expression. If we encounter the operator, we will push it into the stack.

Input expression Stack Prefix expression

Q Q

+ + Q

T + QT

* +* QT

V +* QTV

/ +*/ QTV
U +*/ QTVU

/ +*// QTVU

W +*// QTVUW

* +*//* QTVUW

) +*//*) QTVUW

P +*//*) QTVUWP

^ +*//*)^ QTVUWP

O +*//*)^ QTVUWPO

( +*//* QTVUWPO^

+ ++ QTVUWPO^*//*

N ++ QTVUWPO^*//*N

* ++* QTVUWPO^*//*N

M ++* QTVUWPO^*//*NM

- ++- QTVUWPO^*//*NM*

L ++- QTVUWPO^*//*NM*L

+ ++-+ QTVUWPO^*//*NM*L

K ++-+ QTVUWPO^*//*NM*LK

QTVUWPO^*//*NM*LK+-++

The above expression, i.e., QTVUWPO^*//*NM*LK+-++, is not a final expression. We need to


reverse this expression to obtain the prefix expression.

Rules for the conversion of infix to prefix expression:

 First, reverse the infix expression given in the problem.


 Scan the expression from left to right.
 Whenever the operands arrive, print them.
 If the operator arrives and the stack is found to be empty, then simply push the operator into
the stack.
 If the incoming operator has higher precedence than the TOP of the stack, push the incoming
operator into the stack.
 If the incoming operator has the same precedence with a TOP of the stack, push the incoming
operator into the stack.
 If the incoming operator has lower precedence than the TOP of the stack, pop, and print the top
of the stack. Test the incoming operator against the top of the stack again and pop the operator
from the stack till it finds the operator of a lower precedence or same precedence.
 If the incoming operator has the same precedence with the top of the stack and the incoming
operator is ^, then pop the top of the stack till the condition is true. If the condition is not true,
push the ^ operator.
 When we reach the end of the expression, pop, and print all the operators from the top of the
stack.
 If the operator is ')', then push it into the stack.
 If the operator is '(', then pop all the operators from the stack till it finds ) opening bracket in the
stack.
 If the top of the stack is ')', push the operator on the stack.
 At the end, reverse the output.

Pseudocode of infix to prefix conversion

1. Function InfixtoPrefix( stack, infix)


2. infix = reverse(infix)
3. loop i = 0 to infix.length
4. if infix[i] is operand → prefix+= infix[i]
5. else if infix[i] is '(' → stack.push(infix[i])
6. else if infix[i] is ')' → pop and print the values of stack till the symbol ')' is not found
7. else if infix[i] is an operator(+, -, *, /, ^) →
8.
9. if the stack is empty then push infix[i] on the top of the stack.
10. Else →
11. If precedence(infix[i] > precedence(stack.top))
12. → Push infix[i] on the top of the stack
13. else if(infix[i] == precedence(stack.top) && infix[i] == '^')
14. → Pop and print the top values of the stack till the condition is true
15. → Push infix[i] into the stack
16. else if(infix[i] == precedence(stack.top))
17. → Push infix[i] on to the stack
18. Else if(infix[i] < precedence(stack.top))
19. → Pop the stack values and print them till the stack is not empty and infix[i] < precedence(stack
.top)
20. → Push infix[i] on to the stack
21. End loop
22. Pop and print the remaining elements of the stack
23. Prefix = reverse(prefix)
24. return
Tower of Hanoi in Data Structure
The Tower of Hanoi is a mathematical puzzle containing 3 pillars/towers with n
disks each of a different size/diameter. These disks can slide onto any pillar. The
following diagram shows the initial state of the problem.

Here, we have stacked the disks over each other in the ascending order of their sizes.
Note that we can have any number of disks but only 3 towers.

Rules to be followed:
Our objective in this puzzle is to move all these disks to another pillar without
changing the order in which the disks are placed in the initial state. The rules for this
puzzle are:

1. We can move only one disk at a time.

2. We can remove only the top disk.

3. We can only place a disk above a disk of a larger size.

Approach:
Tower of Hanoi is a recursion based puzzle and thus, we will follow a recursive
approach to solve it. Consider a puzzle with 3 pillars and 3 disks as shown:
Step 1: toh(2, source, aux, dest)

Step 2: Move the disk from source to destination

Step 3: toh(2, aux, dest, source)

Thus, in general, for n disks, the steps are:

1: Move n-1 disks from source to auxiliary i.e. toh(n-1, source, aux, dest)
2: Move the nth disk from source to destination
3: Move n-1 disks from aux to dest i.e. toh(n-1, aux, dest, source)

Algorithm:
We can write the solution for this puzzle in both iterative and recursive approaches.
Let us here see the steps to write a recursive algorithm for the Tower of Hanoi.
procedure TOH(disk, Source, Dest, Aux):
if disk == 1:
move disk from Source to Dest
else:
TOH(disk - 1, Source, Aux, Dest)
move disk from Source to Dest
TOH(disk - 1, Aux, Dest, Source)
END if
END TOH

Implementation of Tower of Hanoi in C:


#include<stdio.h>
void toh(int, char, char, char);
int main(){
char source = 'A', destination='B', Auxiliary='C';
int n;
printf("Enter value of n\t");
scanf("%d", &n);
toh(n, source, destination, Auxiliary);
return 0;
}
void toh(int n, char source, char dest, char aux){
if(n==1){
printf("%c -> %c \n", source, dest);
return;
}
toh(n-1, source, aux, dest);
printf("%c -> %c \n", source, dest);
toh(n-1, aux, dest, source);
}

Queue
1. A queue can be defined as an ordered list which enables insert operations to be performed at
one end called REAR and delete operations to be performed at another end called FRONT.

2. Queue is referred to be as First In First Out list.

3. For example, people waiting in line for a rail ticket form a queue.

Applications of Queue

Due to the fact that queue performs actions on first in first out basis which is quite fair for the
ordering of actions. There are various applications of queues discussed as below.

Queues are widely used as waiting lists for a single shared resource like printer, disk, CPU.

1. Queues are used in asynchronous transfer of data (where data is not being transferred at the
same rate between two processes) for eg. pipes, file IO, sockets.
2. Queues are used as buffers in most of the applications like MP3 media player, CD player, etc.
3. Queue are used to maintain the play list in media players in order to add and remove the songs
from the play-list.
4. Queues are used in operating systems for handling interrupts.
Complexity
Data Structure Time Complexity Space Compleity

Average Worst Worst

Access Search Insertion Deletion Access Search Insertion Deletion

Queue θ(n) θ(n) θ(1) θ(1) O(n) O(n) O(1) O(1) O(n)

Types of Queue
There are four different types of queue that are listed as follows -

 Simple Queue or Linear Queue


 Circular Queue
 Priority Queue
 Double Ended Queue (or Deque)

Let's discuss each of the type of queue.

Simple Queue or Linear Queue


In Linear Queue, an insertion takes place from one end while the deletion occurs from another
end. The end at which the insertion takes place is known as the rear end, and the end at which the
deletion takes place is known as front end. It strictly follows the FIFO rule.

The major drawback of using a linear Queue is that insertion is done only from the rear end. If
the first three elements are deleted from the Queue, we cannot insert more elements even though
the space is available in a Linear Queue. In this case, the linear Queue shows the overflow
condition as the rear is pointing to the last element of the Queue.

To know more about the queue in data structure, you can click the link -
https://www.javatpoint.com/data-structure-queue

Circular Queue

In Circular Queue, all the nodes are represented as circular. It is similar to the linear Queue
except that the last element of the queue is connected to the first element. It is also known as
Ring Buffer, as all the ends are connected to another end. The representation of circular queue is
shown in the below image -

The drawback that occurs in a linear queue is overcome by using the circular queue. If the empty
space is available in a circular queue, the new element can be added in an empty space by simply
incrementing the value of rear. The main advantage of using the circular queue is better memory
utilization.

To know more about the circular queue, you can click the link -
https://www.javatpoint.com/circular-queue

Priority Queue
It is a special type of queue in which the elements are arranged based on the priority. It is a
special type of queue data structure in which every element has a priority associated with it.
Suppose some elements occur with the same priority, they will be arranged according to the
FIFO principle. The representation of priority queue is shown in the below image -

Insertion in priority queue takes place based on the arrival, while deletion in the priority queue
occurs based on the priority. Priority queue is mainly used to implement the CPU scheduling
algorithms.

There are two types of priority queue that are discussed as follows -

 Ascending priority queue - In ascending priority queue, elements can be inserted in arbitrary
order, but only smallest can be deleted first. Suppose an array with elements 7, 5, and 3 in the
same order, so, insertion can be done with the same sequence, but the order of deleting the
elements is 3, 5, 7.
 Descending priority queue - In descending priority queue, elements can be inserted in arbitrary
order, but only the largest element can be deleted first. Suppose an array with elements 7, 3,
and 5 in the same order, so, insertion can be done with the same sequence, but the order of
deleting the elements is 7, 5, 3.

To learn more about the priority queue, you can click the link - https://www.javatpoint.com/ds-
priority-queue

Deque (or, Double Ended Queue)

In Deque or Double Ended Queue, insertion and deletion can be done from both ends of the
queue either from the front or rear. It means that we can insert and delete elements from both
front and rear ends of the queue. Deque can be used as a palindrome checker means that if we
read the string from both ends, then the string would be the same.

Deque can be used both as stack and queue as it allows the insertion and deletion operations on
both ends. Deque can be considered as stack because stack follows the LIFO (Last In First Out)
principle in which insertion and deletion both can be performed only from one end. And in
deque, it is possible to perform both insertion and deletion from one end, and Deque does not
follow the FIFO principle.

The representation of the deque is shown in the below image -


To know more about the deque, you can click the link - https://www.javatpoint.com/ds-deque

There are two types of deque that are discussed as follows –

Input restricted deque - As the name implies, in input restricted queue, insertion operation can
be performed at only one end, while deletion can be performed from both ends.

 Output restricted deque - As the name implies, in output restricted queue, deletion operation
can be performed at only one end, while insertion can be performed from both ends.

Now, let's see the operations performed on the queue.

Operations performed on queue

The fundamental operations that can be performed on queue are listed as follows -

 Enqueue: The Enqueue operation is used to insert the element at the rear end of the queue. It
returns void.
 Dequeue: It performs the deletion from the front-end of the queue. It also returns the element
which has been removed from the front-end. It returns an integer value.
 Peek: This is the third operation that returns the element, which is pointed by the front pointer
in the queue but does not delete it.
 Queue overflow (isfull): It shows the overflow condition when the queue is completely full.
 Queue underflow (isempty): It shows the underflow condition when the Queue is empty, i.e.,
no elements are in the Queue.

Array representation of Queue


We can easily represent queue by using linear arrays. There are two variables i.e. front and rear,
that are implemented in the case of every queue. Front and rear variables point to the position
from where insertions and deletions are performed in a queue. Initially, the value of front and
queue is -1 which represents an empty queue. Array representation of a queue containing 5
elements along with the respective values of front and rear, is shown in the following figure.

The above figure shows the queue of characters forming the English word "HELLO". Since, No
deletion is performed in the queue till now, therefore the value of front remains -1 . However, the
value of rear increases by one every time an insertion is performed in the queue. After inserting
an element into the queue shown in the above figure, the queue will look something like
following. The value of rear will become 5 while the value of front remains same.

After deleting an element, the value of front will increase from -1 to 0. however, the queue will
look something like following.
Algorithm to insert any element in a queue

Check if the queue is already full by comparing rear to max - 1. if so, then return an overflow
error.

If the item is to be inserted as the first element in the list, in that case set the value of front and
rear to 0 and insert the element at the rear end.

Otherwise keep increasing the value of rear and insert each element one by one having rear as
the index.

Algorithm

 Step 1: IF REAR = MAX - 1


Write OVERFLOW
Go to step
[END OF IF]
 Step 2: IF FRONT = -1 and REAR = -1
SET FRONT = REAR = 0
ELSE
SET REAR = REAR + 1
[END OF IF]
 Step 3: Set QUEUE[REAR] = NUM
 Step 4: EXIT

C Function

1. void insert (int queue[], int max, int front, int rear, int item)
2. {
3. if (rear + 1 == max)
4. {
5. printf("overflow");
6. }
7. else
8. {
9. if(front == -1 && rear == -1)
10. {
11. front = 0;
12. rear = 0;
13. }
14. else
15. {
16. rear = rear + 1;
17. }
18. queue[rear]=item;
19. }
20. }

Algorithm to delete an element from the queue

If, the value of front is -1 or value of front is greater than rear , write an underflow message and
exit.

Otherwise, keep increasing the value of front and return the item stored at the front end of the
queue at each time.

Algorithm

 Step 1: IF FRONT = -1 or FRONT > REAR


Write UNDERFLOW
ELSE
SET VAL = QUEUE[FRONT]
SET FRONT = FRONT + 1
[END OF IF]
 Step 2: EXIT

C Function

1. int delete (int queue[], int max, int front, int rear)
2. {
3. int y;
4. if (front == -1 || front > rear)
5.
6. {
7. printf("underflow");
8. }
9. else
10. {
11. y = queue[front];
12. if(front == rear)
13. {
14. front = rear = -1;
15. else
16. front = front + 1;
17.
18. }
19. return y;
20. }
21. }

Menu driven program to implement queue using array

#include<stdio.h>

1. #include<stdlib.h>
2. #define maxsize 5
3. void insert();
4. void delete();
5. void display();
6. int front = -1, rear = -1;
7. int queue[maxsize];
8. void main ()
9. {
10. int choice;
11. while(choice != 4)
12. {
13. printf("\n*************************Main Menu*****************************\n");

14. printf("\n=================================================================\n
");
15. printf("\n1.insert an element\n2.Delete an element\n3.Display the queue\n4.Exit\n");
16. printf("\nEnter your choice ?");
17. scanf("%d",&choice);
18. switch(choice)
19. {
20. case 1:
21. insert();
22. break;
23. case 2:
24. delete();
25. break;
26. case 3:
27. display();
28. break;
29. case 4:
30. exit(0);
31. break;
32. default:
33. printf("\nEnter valid choice??\n");
34. }
35. }
36. }
37. void insert()
38. {
39. int item;
40. printf("\nEnter the element\n");
41. scanf("\n%d",&item);
42. if(rear == maxsize-1)
43. {
44. printf("\nOVERFLOW\n");
45. return;
46. }
47. if(front == -1 && rear == -1)
48. {
49. front = 0;
50. rear = 0;
51. }
52. else
53. {
54. rear = rear+1;
55. }
56. queue[rear] = item;
57. printf("\nValue inserted ");
58.
59. }
60. void delete()
61. {
62. int item;
63. if (front == -1 || front > rear)
64. {
65. printf("\nUNDERFLOW\n");
66. return;
67.
68. }
69. else
70. {
71. item = queue[front];
72. if(front == rear)
73. {
74. front = -1;
75. rear = -1 ;
76. }
77. else
78. {
79. front = front + 1;
80. }
81. printf("\nvalue deleted ");
82. }
83.
84.
85. }
86.
87. void display()
88. {
89. int i;
90. if(rear == -1)
91. {
92. printf("\nEmpty queue\n");
93. }
94. else
95. { printf("\nprinting values .....\n");
96. for(i=front;i<=rear;i++)
97. {
98. printf("\n%d\n",queue[i]);
99. }
100. }
101. }

Output:

*************Main Menu**************

==============================================

1.insert an element
2.Delete an element
3.Display the queue
4.Exit

Enter your choice ?1

Enter the element


123

Value inserted

*************Main Menu**************

==============================================

1.insert an element
2.Delete an element
3.Display the queue
4.Exit

Enter your choice ?1

Enter the element


90

Value inserted
*************Main Menu**************

===================================

1.insert an element
2.Delete an element
3.Display the queue
4.Exit

Enter your choice ?2

value deleted

*************Main Menu**************
==============================================

1.insert an element
2.Delete an element
3.Display the queue
4.Exit

Enter your choice ?3

printing values .....

90

*************Main Menu**************

==============================================

1.insert an element
2.Delete an element
3.Display the queue
4.Exit

Enter your choice ?4


Drawback of array implementation

Although, the technique of creating a queue is easy, but there are some drawbacks of using this
technique to implement a queue.

 Memory wastage : The space of the array, which is used to store queue elements, can never be
reused to store the elements of that queue because the elements can only be inserted at front
end and the value of front might be so high so that, all the space before that, can never be filled.
The above figure shows how the memory space is wasted in the array representation of queue. In
the above figure, a queue of size 10 having 3 elements, is shown. The value of the front variable
is 5, therefore, we can not reinsert the values in the place of already deleted element before the
position of front. That much space of the array is wasted and can not be used in the future (for
this queue).

 Deciding the array size

On of the most common problem with array implementation is the size of the array which
requires to be declared in advance. Due to the fact that, the queue can be extended at runtime
depending upon the problem, the extension in the array size is a time taking process and almost
impossible to be performed at runtime since a lot of reallocations take place. Due to this reason,
we can declare the array large enough so that we can store queue elements as enough as possible
but the main problem with this declaration is that, most of the array slots (nearly half) can never
be reused. It will again lead to memory wastage.

Circular Queue
Why was the concept of the circular queue introduced?

There was one limitation in the array implementation of Queue. If the rear reaches to the end
position of the Queue then there might be possibility that some vacant spaces are left in the
beginning which cannot be utilized. So, to overcome such limitations, the concept of the circular
queue was introduced.
As we can see in the above image, the rear is at the last position of the Queue and front is
pointing somewhere rather than the 0th position. In the above array, there are only two elements
and other three positions are empty. The rear is at the last position of the Queue; if we try to
insert the element then it will show that there are no empty spaces in the Queue. There is one
solution to avoid such wastage of memory space by shifting both the elements at the left and
adjust the front and rear end accordingly. It is not a practically good approach because shifting
all the elements will consume lots of time. The efficient approach to avoid the wastage of the
memory is to use the circular queue data structure.

What is a Circular Queue?

A circular queue is similar to a linear queue as it is also based on the FIFO (First In First Out)
principle except that the last position is connected to the first position in a circular queue that
forms a circle. It is also known as a Ring Buffer.

Operations on Circular Queue

The following are the operations that can be performed on a circular queue:

 Front: It is used to get the front element from the Queue.


 Rear: It is used to get the rear element from the Queue.
 enQueue(value): This function is used to insert the new value in the Queue. The new element is
always inserted from the rear end.
 deQueue(): This function deletes an element from the Queue. The deletion in a Queue always
takes place from the front end.
Applications of Circular Queue

The circular Queue can be used in the following scenarios:

 Memory management: The circular queue provides memory management. As we have already
seen that in linear queue, the memory is not managed very efficiently. But in case of a circular
queue, the memory is managed efficiently by placing the elements in a location which is unused.
 CPU Scheduling: The operating system also uses the circular queue to insert the processes and
then execute them.
 Traffic system: In a computer-control traffic system, traffic light is one of the best examples of
the circular queue. Each light of traffic light gets ON one by one after every jinterval of time. Like
red light gets ON for one minute then yellow light for one minute and then green light. After
green light, the red light gets ON.

Enqueue operation

The steps of enqueue operation are given below:

 First, we will check whether the Queue is full or not.


 Initially the front and rear are set to -1. When we insert the first element in a Queue, front and
rear both are set to 0.
 When we insert a new element, the rear gets incremented, i.e., rear=rear+1.

Scenarios for inserting an element

There are two scenarios in which queue is not full:

 If rear != max - 1, then rear will be incremented to mod(maxsize) and the new value will be
inserted at the rear end of the queue.
 If front != 0 and rear = max - 1, it means that queue is not full, then set the value of rear to 0
and insert the new element there.

There are two cases in which the element cannot be inserted:

 When front ==0 && rear = max-1, which means that front is at the first position of the Queue
and rear is at the last position of the Queue.
 front== rear + 1;

Algorithm to insert an element in a circular queue

Step 1: IF (REAR+1)%MAX = FRONT


Write " OVERFLOW "
Goto step 4
[End OF IF]
Step 2: IF FRONT = -1 and REAR = -1
SET FRONT = REAR = 0
ELSE IF REAR = MAX - 1 and FRONT ! = 0
SET REAR = 0
ELSE
SET REAR = (REAR + 1) % MAX
[END OF IF]

Step 3: SET QUEUE[REAR] = VAL

Step 4: EXIT

Dequeue Operation

The steps of dequeue operation are given below:

 First, we check whether the Queue is empty or not. If the queue is empty, we cannot perform
the dequeue operation.
 When the element is deleted, the value of front gets decremented by 1.
 If there is only one element left which is to be deleted, then the front and rear are reset to -1.

Algorithm to delete an element from the circular queue

Step 1: IF FRONT = -1
Write " UNDERFLOW "
Goto Step 4
[END of IF]

Step 2: SET VAL = QUEUE[FRONT]

Step 3: IF FRONT = REAR


SET FRONT = REAR = -1
ELSE
IF FRONT = MAX -1
SET FRONT = 0
ELSE
SET FRONT = FRONT + 1
[END of IF]
[END OF IF]

Step 4: EXIT

Let's understand the enqueue and dequeue operation through the diagrammatic
representation.
Implementation of circular queue using Array

1. #include <stdio.h>
2.
3. # define max 6
4. int queue[max]; // array declaration
5. int front=-1;
6. int rear=-1;
7. // function to insert an element in a circular queue
8. void enqueue(int element)
9. {
10. if(front==-1 && rear==-1) // condition to check queue is empty
11. {
12. front=0;
13. rear=0;
14. queue[rear]=element;
15. }
16. else if((rear+1)%max==front) // condition to check queue is full
17. {
18. printf("Queue is overflow..");
19. }
20. else
21. {
22. rear=(rear+1)%max; // rear is incremented
23. queue[rear]=element; // assigning a value to the queue at the rear position.
24. }
25. }
26.
27. // function to delete the element from the queue
28. int dequeue()
29. {
30. if((front==-1) && (rear==-1)) // condition to check queue is empty
31. {
32. printf("\nQueue is underflow..");
33. }
34. else if(front==rear)
35. {
36. printf("\nThe dequeued element is %d", queue[front]);
37. front=-1;
38. rear=-1;
39. }
40. else
41. {
42. printf("\nThe dequeued element is %d", queue[front]);
43. front=(front+1)%max;
44. }
45. }
46. // function to display the elements of a queue
47. void display()
48. {
49. int i=front;
50. if(front==-1 && rear==-1)
51. {
52. printf("\n Queue is empty..");
53. }
54. else
55. {
56. printf("\nElements in a Queue are :");
57. while(i<=rear)
58. {
59. printf("%d,", queue[i]);
60. i=(i+1)%max;
61. }
62. }
63. }
64. int main()
65. {
66. int choice=1,x; // variables declaration
67.
68. while(choice<4 && choice!=0) // while loop
69. {
70. printf("\n Press 1: Insert an element");
71. printf("\nPress 2: Delete an element");
72. printf("\nPress 3: Display the element");
73. printf("\nEnter your choice");
74. scanf("%d", &choice);
75.
76. switch(choice)
77. {
78.
79. case 1:
80.
81. printf("Enter the element which is to be inserted");
82. scanf("%d", &x);
83. enqueue(x);
84. break;
85. case 2:
86. dequeue();
87. break;
88. case 3:
89. display();
90.
91. }}
92. return 0;
93. }

Output:
What is a priority queue?
A priority queue is an abstract data type that behaves similarly to the normal queue except that
each element has some priority, i.e., the element with the highest priority would come first in a
priority queue. The priority of the elements in a priority queue will determine the order in which
elements are removed from the priority queue.

The priority queue supports only comparable elements, which means that the elements are either
arranged in an ascending or descending order.

For example, suppose we have some values like 1, 3, 4, 8, 14, 22 inserted in a priority queue
with an ordering imposed on the values is from least to the greatest. Therefore, the 1 number
would be having the highest priority while 22 will be having the lowest priority.

Characteristics of a Priority queue

A priority queue is an extension of a queue that contains the following characteristics:

 Every element in a priority queue has some priority associated with it.
 An element with the higher priority will be deleted before the deletion of the lesser priority.
 If two elements in a priority queue have the same priority, they will be arranged using the FIFO
principle.

Let's understand the priority queue through an example.

We have a priority queue that contains the following values:

1, 3, 4, 8, 14, 22

All the values are arranged in ascending order. Now, we will observe how the priority queue will
look after performing the following operations:

 poll(): This function will remove the highest priority element from the priority queue. In the
above priority queue, the '1' element has the highest priority, so it will be removed from the
priority queue.
 add(2): This function will insert '2' element in a priority queue. As 2 is the smallest element
among all the numbers so it will obtain the highest priority.
 poll(): It will remove '2' element from the priority queue as it has the highest priority queue.
 add(5): It will insert 5 element after 4 as 5 is larger than 4 and lesser than 8, so it will obtain the
third highest priority in a priority queue.

Types of Priority Queue


There are two types of priority queue:

 Ascending order priority queue: In ascending order priority queue, a lower priority number is
given as a higher priority in a priority. For example, we take the numbers from 1 to 5 arranged in
an ascending order like 1,2,3,4,5; therefore, the smallest number, i.e., 1 is given as the highest
priority in a priority queue.

 Descending order priority queue: In descending order priority queue, a higher priority number
is given as a higher priority in a priority. For example, we take the numbers from 1 to 5 arranged
in descending order like 5, 4, 3, 2, 1; therefore, the largest number, i.e., 5 is given as the highest
priority in a priority queue.

Representation of priority queue

Now, we will see how to represent the priority queue through a one-way list.

We will create the priority queue by using the list given below in which INFO list contains the
data elements, PRN list contains the priority numbers of each data element available in the
INFO list, and LINK basically contains the address of the next node.
Let's create the priority queue step by step.

In the case of priority queue, lower priority number is considered the higher priority, i.e.,
lower priority number = higher priority.

Step 1: In the list, lower priority number is 1, whose data value is 333, so it will be inserted in
the list as shown in the below diagram:

Step 2: After inserting 333, priority number 2 is having a higher priority, and data values
associated with this priority are 222 and 111. So, this data will be inserted based on the FIFO
principle; therefore 222 will be added first and then 111.

Step 3: After inserting the elements of priority 2, the next higher priority number is 4 and data
elements associated with 4 priority numbers are 444, 555, 777. In this case, elements would be
inserted based on the FIFO principle; therefore, 444 will be added first, then 555, and then 777.

Step 4: After inserting the elements of priority 4, the next higher priority number is 5, and the
value associated with priority 5 is 666, so it will be inserted at the end of the queue.

You might also like