Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                

Unit 2

Download as pdf or txt
Download as pdf or txt
You are on page 1of 29

UNIT - 2

Stacks & Queues

Er Asim Ahmad
Assistant Professor, CSE
Shri Ramswaroop Memorial College of Engineering &
Management
STACK DATA STRUCTURE
A stack is a linear data structure that follows the principle of Last In First Out (LIFO). This
means the last element inserted inside the stack is removed first. This strategy states that the
element that is inserted last will come out first. You can take a pile of plates kept on top of each
other as a real-life example. The plate we put last is on the top, and since we remove the plate at
the top, we can say that the plate that was put last comes out first.

STACK REPRESENTATION SIMILAR TO A PILE OF


PLATE

In programming terms, putting an item on top of the stack is called pushing the element and removing
an item is called popping the element. So, in Stack, these two operations are very important.

STACK PUSH AND POP OPERATIONS

In order to make manipulations in a stack, there are some basic operations that allow us to perform
different actions on a stack.

 Push: Add an element to the top of a stack


 Pop: Remove an element from the top of a stack
 IsEmpty: Check if the stack is empty
 IsFull: Check if the stack is full
 Peek: Get the value of the top element without removing it
Push
Adds an item to the stack. If the stack is full, then it is said to be an Overflow condition.

 Algorithm for Push operation

begin procedure push


if stack is full
return
else
increment top
stack[top] assign value
endif
end procedure

Pop
Removes an item from the stack. The items are popped in the reversed order in which they are pushed. If
the stack is empty, then it is said to be an Underflow condition.

 Algorithm for Pop operation

begin procedure pop


if stack is empty
return
else
store value of stack[top]
decrement top
return value
endif
end procedure

IsEmpty
Returns true if the stack is empty, else false.

 Algorithm for IsEmpty operation


begin procedure isEmpty
if top < 1
return true
else
return false
endif
end procedure

IsFull
Removes an item from the stack. The items are popped in the reversed order in which they are pushed. If
the stack is empty, then it is said to be an Underflow condition.

 Algorithm for IsFull operation

begin procedure isFull


if top equals to MAXSIZE
return true
else
return false
endif
end procedure

Peek
Returns the top or peek element of the stack.

 Algorithm for Peek operation

begin procedure peek


return stack[top]
end procedure

For a better understanding of Stack, here is a sample program for the push() and pop() functions in Stack as
follows:
C PROGRAM FOR IMPLEMENTATION OF STACK

// Program for linked list implementation of stack in C language


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

// A structure to represent a stack


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

struct StackNode* newNode(int data)


{
struct StackNode* stackNode = (struct StackNode*) malloc(sizeof(struct
StackNode)); stackNode->data = data;
stackNode->next =
NULL; return stackNode;
}

int isEmpty(struct StackNode* root)


{
return !root;
}

void push(struct StackNode** root, int data)


{
struct StackNode* stackNode = newNode(data);
stackNode->next = *root;
*root = stackNode;
printf("%d pushed to stack\n", data);
}

int pop(struct StackNode** root)


{
if (isEmpty(*root))
return
INT_MIN;
struct StackNode* temp = *root;
*root = (*root)->next;
int popped = temp-
>data;free(temp);

return popped;
}
int peek(struct StackNode* root)
{
if (isEmpty(root))
return
INT_MIN;
return root->data;
}

int main()
{
struct StackNode* root = NULL;

push(&root, 10);
push(&root, 20);
push(&root, 30);

printf("%d popped from stack\n", pop(&root));

printf("Top element is %d\n", peek(root));

return 0;
}

Types of Stacks:
 Fixed Size Stack: As the name suggests, a fixed size stack has a fixed size and cannot grow or shrink
dynamically. If the stack is full and an attempt is made to add an element to it, an overflow error
occurs. If the stack is empty and an attempt is made to remove an element from it, an underflow error
occurs.
 Dynamic Size Stack: A dynamic size stack can grow or shrink dynamically. When the stack is full, it
automatically increases its size to accommodate the new element, and when the stack is empty, it
decreases its size. This type of stack is implemented using a linked list, as it allows for easy resizing
of the stack.
In addition to these two main types, there are several other variations of Stacks, including:
1. Infix to Postfix Stack: This type of stack is used to convert infix expressions to postfix expressions.
2. Expression Evaluation Stack: This type of stack is used to evaluate postfix expressions.
3. Recursion Stack: This type of stack is used to keep track of function calls in a computer program and
to return control to the correct function when a function returns.
4. Memory Management Stack: This type of stack is used to store the values of the program counter
and the values of the registers in a computer program, allowing the program to return to the previous
state when a function returns.
5. Balanced Parenthesis Stack: This type of stack is used to check the balance of parentheses in an
expression.
6. Undo-Redo Stack: This type of stack is used in computer programs to allow users to undo and redo
actions.
Applications of the stack:
 Infix to Postfix /Prefix conversion
 Redo-undo features at many places like editors, photoshop.
 Forward and backward features in web browsers
 Used in many algorithms like Tower of Hanoi, tree traversals, stock span problems, and histogram
problems.
 Backtracking is one of the algorithm designing techniques. Some examples of backtracking are the
Knight-Tour problem, N-Queen problem, find your way through a maze, and game-like chess or
checkers in all these problems we dive into someway if that way is not efficient we come back to the
previous state and go into some another path. To get back from a current state we need to store the
previous state for that purpose we need a stack.
 In Graph Algorithms like Topological Sorting and Strongly Connected Components
 In Memory management, any modern computer uses a stack as the primary management for a running
purpose. Each program that is running in a computer system has its own memory allocations
 String reversal is also another application of stack. Here one by one each character gets inserted into
the stack. So the first character of the string is on the bottom of the stack and the last element of a
string is on the top of the stack. After Performing the pop operations on the stack we get a string in
reverse order.
 Stack also helps in implementing function call in computers. The last called function is always
completed first.
 Stacks are also used to implement the undo/redo operation in text editor.
Implementation of Stack:
A stack can be implemented using an array or a linked list. In an array-based implementation, the push
operation is implemented by incrementing the index of the top element and storing the new element at
that index. The pop operation is implemented by decrementing the index of the top element and
returning the value stored at that index. In a linked list-based implementation, the push operation is
implemented by creating a new node with the new element and setting the next pointer of the current top
node to the new node. The pop operation is implemented by setting the next pointer of the current top
node to the next node and returning the value of the current top node.
Stacks are commonly used in computer science for a variety of applications, including the evaluation of
expressions, function calls, and memory management. In the evaluation of expressions, a stack can be
used to store operands and operators as they are processed. In function calls, a stack can be used to keep
track of the order in which functions are called and to return control to the correct function when a
function returns. In memory management, a stack can be used to store the values of the program counter
and the values of the registers in a computer program, allowing the program to return to the previous
state when a function returns.
In conclusion, a Stack is a linear data structure that operates on the LIFO principle and can be
implemented using an array or a linked list. The basic operations that can be performed on a stack
include push, pop, and peek, and stacks are commonly used in computer science for a variety of
applications, including the evaluation of expressions, function calls, and memory management.There are
two ways to implement a stack –
 Using array
 Using linked list

Prefix and Postfix Expressions in Data Structure


The way to write arithmetic expression is known as a notation. An arithmetic expression can be written in
three different but equivalent notations, i.e., without changing the essence or output of an expression.
These notations are –
 Infix
 Prefix
 Postfix
Infix notations are normal notations, that are used by us while write different mathematical expressions.
The Prefix and Postfix notations are quite different.
Prefix Notation
An infix expression is an expression in which the operators are written between the two operands. If we
move the operator before the operands then it is known as a prefix expression. In other words, prefix
expression can be defined as an expression in which all the operators precede the two operands. For
example, +ab. This is equivalent to its infix notation a + b. Prefix notation is also known as Polish
Notation.
Example

Expression Infix Prefix Postfix


No Notation Notation Notation

1 a+b +ab ab+

2 (a + b) * c *+abc ab+c*

3 a * (b + c) *a+bc abc+*

4 a/b+c/d +/ab/cd ab/cd/+

5 (a + b) * (c + *+ab+cd ab+cd+*
d)

6 ((a + b) * c) - -*+abcd ab+c*d-


d

Parsing Expression
As we have discussed, it is not a very efficient way to design an algorithm or program to parse infix
notations. Instead, these infix notations are first converted into either postfix or prefix notations and then
computed. To parse any arithmetic expression, we need to take care of operator precedence and
associativity also.

If the infix expression is given as: A + B * C

As we know that the multiplication operator * has a higher precedence than the addition operator. First,
multiplication operator will move before operand B shown as below:

A+*BC

Once the multiplication operator is moved before 'B' operand, addition operator will move before the
operand 'A' shown as below:

+A*BC

Evaluation of Prefix Expression using Stack

Step 1: Initialize a pointer 'S' pointing to the end of the expression.

Step 2: If the symbol pointed by 'S' is an operand then push it into the stack.

Step 3: If the symbol pointed by 'S' is an operator then pop two operands from the stack. Perform the
operation on these two operands and stores the result into the stack.

Step 4: Decrement the pointer 'S' by 1 and move to step 2 as long as the symbols left in the expression.
Step 5: The final result is stored at the top of the stack and return it.

Step 6: End

Let's understand the evaluation of prefix expression through an example.

Expression: +, -, *, 2, 2, /, 16, 8, 5

First, we will reverse the expression given above.

Expression: 5, 8, 16, /, 2, 2, *, -, +

We will use the stack data structure to evaluate the prefix expression.

Symbol Scanned Stack

5 5
8 5, 8
16 5, 8, 16
/ 5, 2
2 5, 2, 2
2 5, 2, 2, 2
* 5, 2, 4
- 5, 2
+ 7

The final result of the above expression is 7.

Postfix Notation
If we move the operators after the operands then it is known as a postfix expression. In other words,
postfix expression can be defined as an expression in which all the operators are present after the
operands. This notation style is known as Reversed Polish Notation. In this notation style, the operator
is postfixed to the operands i.e., the operator is written after the operands. For example, ab+. This is
equivalent to its infix notation a + b.
For example:
If the infix expression is A + B * C
As we know that the multiplication operator has a higher precedence than the addition operator, so
multiplication operator will move after the operands B and C shown as below:
A+BC*
Once the multiplication operator is moved after the operand C, then the addition operator will come after
the multiplication operator shown as below:
ABC*+
Evaluation of Postfix expression using Stack
Algorithm for the evaluation of postfix expression using stack:
Step 1: Create an empty stack used for storing the operands.
Step 2: Scan each element of an expression one be one and do the following:
o If the element is an operand then push it into the stack.
o If the element is an operator then pop two operands from the stack. Perform operation on these
operands. Push the final result into the stack.
Step 3: When the expression is scanned completely, the value available in the stack would be the final
output of the given expression.
Let's understand the evaluation of postfix expression using stack through an example.
If the expression is: 5, 6, 2, +, *, 12, 4, /, -
Symbol Scanned Stack

5 5
6 5, 6
2 5, 6, 2
+ 5, 8
* 40
12 40, 12
4 40, 12, 4
/ 40, 3
- 37
The result of the above expression is 37.

Convert infix to prefix notation

Parsing Infix expressions

In order to parse any expression, we need to take care of two things, i.e., Operator
precedence and Associativity. Operator precedence means the precedence of any operator over another
operator. For example:

A + B * C → A + (B * C)

As the multiplication operator has a higher precedence over the addition operator so B * C expression will
be evaluated first. The result of the multiplication of B * C is added to the A.

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

Rules for the conversion of infix to prefix expression:

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


o Scan the expression from left to right.
o Whenever the operands arrive, print them.
o If the operator arrives and the stack is found to be empty, then simply push the operator into the
stack.
o If the incoming operator has higher precedence than the TOP of the stack, push the incoming
operator into the stack.
o If the incoming operator has the same precedence with a TOP of the stack, push the incoming
operator into the stack.
o 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.
o 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.
o When we reach the end of the expression, pop, and print all the operators from the top of the stack.
o If the operator is ')', then push it into the stack.
o If the operator is '(', then pop all the operators from the stack till it finds ) opening bracket in the
stack.
o If the top of the stack is ')', push the operator on the stack.
o At the end, reverse the output.
Example: 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 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 *-
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+.

Precedence order and Associativity of Operators

Precedence Type Operators Associativity

1 Postfix () [] -> . ++ — Left to Right

+ – ! ~ ++ —
2 Unary Right to Left
(type)* & sizeof

3 Multiplicative */% Left to Right

4 Additive +– Left to Right

5 Shift <<, >> Left to Right

6 Relational < <= > >= Left to Right

7 Equality == != Left to Right

8 Bitwise AND & Left to Right


9 Bitwise XOR ^ Left to Right

10 Bitwise OR | Left to Right

11 Logical AND && Left to Right

12 Logical OR || Left to Right

13 Conditional ?: Right to Left

= += -+ *= /=
14 Assignment %= >>= <<= Right to Left
&= ^= |=

15 Comma , Left to Right

Conversion of Prefix to Postfix Expression

Here, we will see the conversion of prefix to postfix expression using a stack data structure.

Rules for prefix to postfix expression using stack data structure:

o Scan the prefix expression from right to left, i.e., reverse.


o If the incoming symbol is an operand then push it into the stack.
o If the incoming symbol is an operator then pop two operands from the stack. Once the operands are
popped out from the stack, we add the incoming symbol after the operands. When the operator is added
after the operands, then the expression is pushed back into the stack.
o Once the whole expression is scanned, pop and print the postfix expression from the stack.

Let's understand the conversion of Prefix to Postfix expression using Stack through an example.

If the expression is: * - A / B C - / A K L

Symbols Action Stack Description


to be
scanned

L Push L into the stack L


K Push K into the stack L, K
A Push A into the stack L, K, A
/ Pop A from the stack L, A K / Pop two operands from the stack, i.e., A and
Pop K from the stack K. Add '/' operator after K operand, i.e., AK/.
Push A K / into the stack Push AK/ into the stack.
- Pop A K / and L from the A K / L - Pop two operands from the stack, i.e., AK/ and
stack. L. Add '-' operator after 'L' operand.
Push (A K / L -) into the
stack
C Push C into the stack AK/L-, C
B Push B into the stack AK/L-, C,
B
/ Pop B and C from the stack. AK/L-, BC/ Pop two operands from the stack, i.e., B and
Push BC/ into the stack. C. Add '/' operator after C operator, i.e., BC/.
Push BC/ into the stack.
A Push A into the stack AK/L-,
BC/, A
- Pop BC/ and A from the AK/L-, Pop two operands from the stack, i.e., A and
stack. Push ABC/- into the ABC/- BC/. Add '-' operator after '/'.
stack.
* Pop ABC/- and AK/L- from ABC/- Pop two operands from the stack, i.e., ABC/-,
the stack. Push ABC/AK/L-* AK/L-* and AK/L- . Add '*' operator after L and '-'
into the stack. operator, i.e., ABC/-AK/L-*.

Conversion of Postfix to Prefix expression

There are two ways of converting a postfix into a prefix expression:

1. Conversion of Postfix to Prefix expression manually.


2. Conversion of Postfix to Prefix expression using stack.

Conversion of Postfix to Prefix expression manually

The following are the steps required to convert postfix into prefix expression:

o Scan the postfix expression from left to right.


o Select the first two operands from the expression followed by one operator.
o Convert it into the prefix format.
o Substitute the prefix sub expression by one temporary variable
o Repeat this process until the entire postfix expression is converted into prefix expression.

Let's understand through an example.

ab-c+

First, we scan the expression from left to right. We will move '-' operator before the operand ab.

-abc+
The next operator '+' is moved before the operand -abc is shown as below:

+-abc

Conversion of Postfix to Prefix expression using Stack

The following are the steps used to convert postfix to prefix expression using stack:

o Scan the postfix expression from left to right.


o If the element is an operand, then push it into the stack.
o If the element is an operator, then pop two operands from the stack.

Create an expression by concatenating two operands and adding operator before the operands.

Push the result back to the stack.

o Repeat the above steps until we reach the end of the postfix expression.

Let's understand the conversion of postfix to prefix expression using stack.

If the Postfix expression is given as:

AB + CD - *

Symbol
Action Stack Description
Scanned
A Push A into the stack A
B Push B into the stack AB
Pop B from the stack +AB Pop two operands from the stack, i.e., A and B.
+ Pop A from the stack Add '+' operator before the operands AB, i.e.,
Push +AB into the stack. +AB.
C Push C into the stack +ABC
D Push D into the stack +ABCD
Pop D from the stack. +AB -CD Pop two operands from the stack, i.e., D and C.
- Pop C from the stack. Add '-' operator before the operands CD, i.e., -CD.
Push -CD into the stack
Pop -CD from the stack. *+AB - Pop two operands from the stack, i.e., -CD and
Pop +AB from the stack. CD +AB. Add '*' operator before +AB then the
* expression would become *+AB-CD.
Push *+AB -CD into the
stack.

The prefix expression of the above postfix expression is *+AB-CD.

RECURSION
The process in which a function calls itself directly or indirectly is called recursion and the corresponding
function is called a recursive function. Using a recursive algorithm, certain problems can be solved quite
easily. Examples of such problems are Towers of Hanoi (TOH), Inorder/Preorder/Postorder Tree
Traversals, DFS of Graph, etc. A recursive function solves a particular problem by calling a copy of
itself and solving smaller sub problems of the original problems. Many more recursive calls can be
generated as and when required. We must know that we should provide a certain case to terminate this
recursion process. So, we can say that the function calls itself a simpler version of the original problem
every time. Recursion is an amazing technique with the help of which we can reduce the length of our
code and make it easier to read and write. It has certain advantages over the iteration technique which
will be discussed later. Recursion is one of the best solutions for a task that can be defined with its
similar subtask. For example; The Factorial of a number. A simple example of recursion would be:

C PROGRAM FOR FIBONACCI NUMBER USING


RECURSION

// Fibonacci Number using


Recursion #include <stdio.h>

int fib(int n)
{
if (n <= 1)
return n;
return fib(n - 1) + fib(n - 2);
}
int main()
{
int n;
printf("Which number do you want? ");
scanf("%d", &n);
printf("%d", fib(n));
return 0;
}

SIMULATION OF RECURSION
Simulation of a system is the operation of a model in terms of time or space, which helps analyse the
performance of an existing or proposed system. In other words, simulation is the process of using a
model to study the performance of a system. In our unit, recursion is the most important topic which we
have to observe through simulation.
A recursive function (or algorithm or method or procedure or routine) is a function (or algorithm or
method or procedure or routine) that calls/uses itself to get something done. Problems that can be broken
down into smaller problems of the same type can often be easily solved using recursion. Consider, for
example, a tree:

FORM OF A TREE USING RECURSION


We can think of a tree as a simple “Y” shape, with smaller “Y” shapes at the tips of that “Y,” and even
smaller “Y” shapes at the tips of those “Y” shapes, etc.

Tail Recursion

Tail recursion is defined as a recursive function in which the recursive call is the last statement that is
executed by the function. So basically nothing is left to execute after the recursion call.

For example the following C++ function print() is tail recursive.

// An example of tail recursive function

static void print(int n)


{
if (n < 0)
return;
cout << " " << n;
// The last executed statement is recursive call
print(n - 1);
}
The tail recursive functions are considered better than non-tail recursive functions as tail-recursion can
be optimized by the compiler. The idea used by compilers to optimize tail-recursive functions is
simple, since the recursive call is the last statement, there is nothing left to do in the current function,
so saving the current function’s stack frame is of no use.

Difference between Recursion and Iteration


Property Recursion Iteration
A set of instructions repeatedly
Function calls itself.
Definition executed.
Application For functions. For loops.
When the termination condition
Through base case, where there will be no
for the iterator ceases to be
function call.
Termination satisfied.
Used when time complexity needs
Used when code size needs to be small, and time
to be balanced against an
complexity is not an issue.
Usage expanded code size.
Code Size Smaller code size Larger Code Size.
Relatively lower time
Time Very high(generally exponential) time
complexity(generally polynomial-
Complexity complexity.
logarithmic).
Space The space complexity is higher than iterations. Space complexity is lower.
Complexity
Here the stack is used to store local variables
Stack is not used.
Stack when the function is called.
Normally, it is faster than
Execution is slow since it has the overhead of
recursion as it doesn’t utilize the
maintaining and updating the stack.
Speed stack.
Iteration uses less memory as
Recursion uses more memory as compared to compared to recursion.
iteration.
Memory
No overhead as there are no
Possesses overhead of repeated function calls.
Overhead function calls in iteration.
If the control condition of the
If the recursive function does not meet to a iteration statement never becomes
termination condition or the base case is not false or the control variable does
Infinite
defined or is never reached then it leads to a stack not reach the termination value,
Repetition
overflow error and there is a chance that the an then it will cause infinite loop. On
system may crash in infinite recursion. the infinite loop, it uses the CPU
cycles again and again.

Replace Recursion with Iteration

Recursion Iteration

public void countDown(int n) { public void countDown(int n) {


if(n == 0) return; while(n > 0) {
System.out.println(n + "...");
System.out.println(n + "..."); waitASecond ();
waitASecond(); n -= 1;
countDown(n-1); }
}

Fibonacci Series using recursion in C

1. #include<stdio.h>
2. void printFibonacci(int n){
3. static int n1=0,n2=1,n3;
4. if(n>0){
5. n3 = n1 + n2;

6. n1 = n2;
7. n2 = n3;
8. printf("%d ",n3);
9. printFibonacci(n-1);
10. }
11. }
12. int main(){
13. int n;
14. printf("Enter the number of elements: ");
15. scanf("%d",&n);
16. printf("Fibonacci Series: ");
17. printf("%d %d ",0,1);
18. printFibonacci(n-2);//n-2 because 2 numbers are already printed
19. return 0;
20. }
TOWER OF HANOI PROBLEM

Tower of Hanoi, is a mathematical puzzle which consists of three towers (pegs) and more than one rings is
as depicted:

These rings are of different sizes and stacked upon in an ascending order, i.e. the smaller one sits over the
larger one. There are other variations of the puzzle where the number of disks increase, but the tower count
remains the same.

Rules

The mission is to move all the disks to some another tower without violating the sequence of arrangement.
A few rules to be followed for Tower of Hanoi are −

 Only one disk can be moved among the towers at any given time.
 Only the "top" disk can be removed.
 No large disk can sit over a small disk.

Tower of Hanoi puzzle with n disks can be solved in minimum 2n−1 steps. This presentation shows that a
puzzle with 3 disks has taken 23 - 1 = 7 steps.
Algorithm
To write an algorithm for Tower of Hanoi, first we need to learn how to solve this problem with lesser
amount of disks, say → 1 or 2. We mark three towers with name, source, destination and aux (only to
help moving the disks). If we have only one disk, then it can easily be moved from source to
destination peg.

If we have 2 disks −
 First, we move the smaller (top) disk to aux peg.
 Then, we move the larger (bottom) disk to destination peg.
 And finally, we move the smaller disk from aux to destination peg.

So now, we are in a position to design an algorithm for Tower of Hanoi with more than two disks. We
divide the stack of disks in two parts. The largest disk (nth disk) is in one part and all other (n-1) disks
are in the second part. Our ultimate aim is to move disk n from source to destination and then put all
other (n1) disks onto it. We can imagine to apply the same in a recursive way for all given set of disks.

The steps to follow are –


Step 1 − Move n-1 disks from source to aux
Step 2 − Move nth disk from source to dest
Step 3 − Move n-1 disks from aux to dest

A recursive algorithm for Tower of Hanoi can be driven as follows –


START
Procedure Hanoi(disk, source, dest, aux)
IF disk == 1, THEN
move disk from source to dest
ELSE
Hanoi(disk - 1, source, aux, dest) // Step 1
move disk from source to dest // Step 2
Hanoi(disk - 1, aux, dest, source) // Step 3
END IF
END Procedure
STOP

QUEUE DATA STRUCTURE

Similar to Stack, Queue is a linear data structure that follows a particular order in which the operations are
performed for storing data. The order is First In First Out (FIFO). One can imagine a queue as a line of
people waiting to receive something in sequential order which starts from the beginning of the line. It is an
ordered list in which insertions are done at one end which is known as the rear and deletions are done from
the other end known as the front. A good example of a queue is any queue of consumers for a resource
where the consumer that came first is served first. The difference between stacks and queues is in
removing. In a stack we remove the item the most recently added; in a queue, we remove the item the least
recently added.
Basic Operations on Queue:
 enqueue(): Inserts an element at the end of the queue i.e. at the rear end.
 dequeue(): This operation removes and returns an element that is at the front end of the queue.
 front(): This operation returns the element at the front end without removing it.
 rear(): This operation returns the element at the rear end without removing it.
 isEmpty(): This operation indicates whether the queue is empty or not.
 isFull(): This operation indicates whether the queue is full or not.
 size(): This operation returns the size of the queue i.e. the total number of elements it contains.
Types of Queues:
 Simple Queue: Simple queue also known as a linear queue is the most basic version of a queue. Here,
insertion of an element i.e. the Enqueue operation takes place at the rear end and removal of an element
i.e. the Dequeue operation takes place at the front end. Here problem is that if we pop some item from
front and then rear reach to the capacity of the queue and although there are empty spaces before front
means the queue is not full but as per condition in isFull() function, it will show that the queue is full
then. To solve this problem we use cirrrcular queue.
 Circular Queue: In a circular queue, the element of the queue act as a circular ring. The working of a
circular queue is similar to the linear queue except for the fact that the last element is connected to the
first element. Its advantage is that the memory is utilized in a better way. This is because if there is an
empty space i.e. if no element is present at a certain position in the queue, then an element can be easily
added at that position using modulo capacity(%n).
 Priority Queue: This queue is a special type of queue. Its specialty is that it arranges the elements in a
queue based on some priority. The priority can be something where the element with the highest value
has the priority so it creates a queue with decreasing order of values. The priority can also be such that
the element with the lowest value gets the highest priority so in turn it creates a queue with increasing
order of values. In pre-define priority queue, C++ gives priority to highest value whereas Java gives
priority to lowest value.
 Dequeue: Dequeue is also known as Double Ended Queue. As the name suggests double ended, it
means that an element can be inserted or removed from both ends of the queue, unlike the other queues
in which it can be done only from one end. Because of this property, it may not obey the First In First
Out property.

Applications of Queue:
Queue is used when things don’t have to be processed immediately, but have to be processed
in First In First Out order like Breadth First Search. This property of Queue makes it also useful in
following kind of scenarios.
 When a resource is shared among multiple consumers. Examples include CPU scheduling, Disk
Scheduling.
 When data is transferred asynchronously (data not necessarily received at same rate as sent) between
two processes. Examples include IO Buffers, pipes, file IO, etc.
 Queue can be used as an essential component in various other data structures.

Array implementation Of Queue:


 For implementing queue, we need to keep track of two indices, front and rear. We enqueue an item at
the rear and dequeue an item from the front. If we simply increment front and rear indices, then there
may be problems, the front may reach the end of the array. The solution to this problem is to increase
front and rear in circular manner.
Steps for enqueue:
 Check the queue is full or not
 If full, print overflow and exit
 If queue is not full, increment tail and add the element
Steps for dequeue:
 Check queue is empty or not
 if empty, print underflow and exit
 if not empty, print element at the head and increment head

Queue – Linked List Implementation


Approach: To solve the problem follow the below idea:
we maintain two pointers, front, and rear. The front points to the first item of the queue and rear points to
the last item.
 enQueue(): This operation adds a new node after the rear and moves the rear to the next node.
 deQueue(): This operation removes the front node and moves the front to the next node.
Follow the below steps to solve the problem:
 Create a class QNode with data members integer data and QNode* next
 A parameterized constructor that takes an integer x value as a parameter and sets data equal
to x and next as NULL
 Create a class Queue with data members QNode front and rear
 Enqueue Operation with parameter x:
 Initialize QNode* temp with data = x
 If the rear is set to NULL then set the front and rear to temp and return(Base Case)
 Else set rear next to temp and then move rear to temp
 Dequeue Operation:
 If the front is set to NULL return(Base Case)
 Initialize QNode temp with front and set front to its next
 If the front is equal to NULL then set the rear to NULL
 Delete temp from the memory
Enqueue:
Queues maintain two data pointers, front and rear. Therefore, its operations are comparatively more
difficult to implement than that of stacks. The following steps should be taken to enqueue (insert) data
into a queue:
 Algorithm for Enqueue operation

begin procedure enqueue(data)if


queue is full
return overflow
endif
rear ← rear + 1
queue[rear] ← data
return true
end procedure

Dequeue:
Accessing data from the queue is a process of two tasks: accessing the data where the front is pointing
and removing the data after access. The following steps are taken to perform the dequeue operation:
 Algorithm for Dequeue operation
begin procedure dequeue
if queue is empty
return underflow
end if
data = queue[front]
front ← front + 1
return true
end procedure

IsEmpty:
While accessing the elements from a queue, we must check whether the queue is empty. If the value ofthe
front is less than MIN or 0, it tells that the queue is not yet initialized, hence empty:
 Algorithm for IsEmpty operation

begin procedure isempty


if front is less than MIN OR front is greater than rear
return true
else
return false
endif
end procedure

IsFull:
As we are using a single dimension array to implement a queue, we just check for the rear pointer to
reach MAXSIZE to determine that the queue is full. In case we maintain the queue in a circular linked list,
the algorithm will differ. Algorithm of isfull() function:
 Algorithm for IsFull operation

begin procedure isfull


if rear equals to MAXSIZE
return true
else
return falseendif
end procedure

Peek:
This function helps to see the data at the front of the queue. The algorithm is as follows:

 Algorithm for Peek operation


begin procedure peek
return queue[front]
end procedure

C PROGRAM FOR IMPLEMENTATION OF QUEUE

#include < stdio.h


> #include <
stdlib.h >

// Structure to create a node with data and the next pointer struct
node {
int data;
struct node * next;
};

struct node * front =


NULL; struct node * rear =
NULL;

// Enqueue() operation on a queue


void enqueue(int value) {
struct node * ptr;
ptr = (struct node * ) malloc(sizeof(struct node));
ptr - > data = value;
ptr - > next = NULL;
if ((front == NULL) && (rear ==
NULL)) {front = rear = ptr;
} else {
rear - > next =
ptr; rear = ptr;
}
printf("Node is Inserted\n\n");
}

// Dequeue() operation on a queue


int dequeue() {
if (front == NULL) {
printf("\nUnderflow\n");
return -1;
} else {
struct node * temp = front;
int temp_data = front - >
data; front = front - > next;
free(temp);
return temp_data;
}
}
// Display all elements of the queue
void display() {
struct node * temp;
if ((front == NULL) && (rear ==
NULL)) { printf("\nQueue is
Empty\n");
} else {
printf("The queue is \n");
temp = front;
while (temp) {
printf("%d--->", temp - >
data); temp = temp - > next;
}
printf("NULL\n\n");
}
}

int main() {
int choice, value;
printf("\nImplementation of Queue using Linked List\n");
while (choice != 4) {
printf("1.Enqueue\n2.Dequeue\n3.Display\n4.Exit\n"); printf("\nEnter
your choice : ");
scanf("%d", &
choice); switch
(choice) {
case 1:
printf("\nEnter the value to insert: ");
scanf("%d", & value);
enqueue(value);
break;
case 2:
printf("Popped element is :%d\n", dequeue());
break;
case 3:
display()
; break;
case 4:
exit(0)
;
break;
default:
printf("\nWrong Choice\n");
}
}
return 0;
}
DEQUEUE (OR) DEQUE (DOUBLE-ENDED QUEUE)
Deque or Double Ended Queue is a type of queue in which insertion and removal of elements can either
be performed from the front or the rear. Thus, it does not follow the FIFO rule (First In First Out). So
Deque is a data structure that inherits the properties of both queues and stacks.

REPRESENTATION OF DEQUE

PRIORITY QUEUE
A priority queue is a special type of queue in which each element is associated with a priority value. And,
elements are served on the basis of their priority. That is, higher-priority elements are served first.
However, if elements with the same priority occur, they are served according to their order in the queue. It
is having a list of items in which each item has associated priority. It works on a principle that adds an
element to the queue with an associated priority and removes the element from the queue that has the
highest priority. In general, different items may have different priorities. In this queue highest or the
lowest priority item are inserted in random order. It is possible to delete an element from a priority
queue in order of their priorities starting with the highest priority.
Generally, the value of the element itself is considered for assigning priority. For example, the element
with the highest value is considered the highest-priority element. However, in other cases, we can
assume the element with the lowest value is the highest priority element.

REMOVING HIGHEST PRIORITY ELEMENT


2.6.1 DIFFERENCE BETWEEN PRIORITY QUEUE AND NORMAL QUEUE
A queue is a data structure containing some elements and the only way the structure changes is by
popping elements off of the front of the queue or pushing elements onto the back. Elements are popped off
in the order that they’re pushed, so for example the first element that’s pushed will be the first element
that’s popped, and the last will be the last.
On the other hand, in a priority queue, each element has some type of value that can be ordered
concerning other values of the same type (like a number, or a string). As elements are pushed onto the
back or popped off of the front, the priority queue re-orders itself to make the front of the queue contain the
element with the smallest value (or the largest, if the priority is defined to be in descending order). So, if
you pop all elements of a priority queue, their values will always come out in ascending or
descending order. But if you do that with a queue — elements, come out in FIFO (first in first out) order.

🙤*🙤

You might also like