0% found this document useful (0 votes)
21 views745 pages

DSA Unit 3, 4, 5 (1)

Uploaded by

signtanviraina
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)
21 views745 pages

DSA Unit 3, 4, 5 (1)

Uploaded by

signtanviraina
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/ 745

21CSC201J

DATA STRUCTURES AND


ALGORITHMS

UNIT-3
Topic : Introduction to Stack
Stack
•It is a linear data structure.
•Elements are accessed or put in a memory
area that is continuous.
Stack
•Array – access data in constant time
•Linked List – sequential access
•Stack – Limited access
Ordered List
•collection or container, follows a principle for
insertion and deletion of the data.
Principle:
•insertion and deletion is possible only from
one end
Stack - Example
Stack - Example
•LIFO (Last In
First Out)
Stack - Operations

STACK
Stack - Operations
PUSH

STACK
Stack - Operations
POP

STACK
Stack - Operations
PUSH POP
TOP

STACK
Stack - Operations
•Push(x)
•Pop()
•Peek() / top()
•isEmpty()
•isFull()
Stack - Operations
•PUSH (x)

2
Stack - Operations
•PUSH (x)

x
2
Stack - Operations
•PUSH (x)

13.5
x
2
Stack - Operations
•PUSH (x)

13.5 13
x 4
2 2
Stack - Operations
•POP ()

13
4
2
Stack - Operations
•Peek ()It returns the element at the given position. Pop ()

3 3
2 2
1 1
Stack - Operations
•isEmpty()
true if the stack is empty
Stack - Operations
•isFull()

true if the stack is full


Logical Representation of Stack

2 ways of implementation
•Static memory allocation – arrays
•Dynamic memory allocation – linked
list
Logical Representation of Stack
Logical Representation of Stack

Size = 5

Stack
Logical Representation of Stack

Size = 5

Stack
Logical Representation of Stack
Size = 5
4
3
2
1
0

Stack
Logical Representation of Stack
Size = 5
4
3
2
1
top = -1 0

Stack
Logical Representation of Stack
Size = 5
Pop()
4
3 underflow
2
1
top = -1 0

Stack
Logical Representation of Stack
Size = 5 Push(2)
4
3
2
1
top = -1 0

Stack
Logical Representation of Stack
Size = 5 Push(2)
4
top++
3
2 top = 0
1
top 0

Stack
Logical Representation of Stack
Size = 5 Push(2)
4
top++
3
2 top = 0
1
top 0 2

Stack
Logical Representation of Stack
Size = 5 Push(3)
4
top++
3
2 top = 1
1
top 0 2

Stack
Logical Representation of Stack
Size = 5 Push(3)
4
top++
3
2 top = 1
1 3
top 0 2

Stack
Logical Representation of Stack
Size = 5 Pop(2)
4
3
2
1 3
top 0 2

Stack
Logical Representation of Stack
Size = 5 Pop(2)
4
3
2
1 3
top 0 2

Stack
Logical Representation of Stack
Size = 5 Pop()
4
top--
3
2
1
top 0 2

Stack
Logical Representation of Stack
Size = 5 Pop()
4
top--
3
2
1
top = -1 0 2

Stack
Logical Representation of Stack
Size = 5 Push(4)
4
top++
3
2
1
top 0 4

Stack
Logical Representation of Stack
Size = 5 Push(1)
4
Push(5)
3
2 Push(6)
1
top 4
Push(7)
0

Stack
Logical Representation of Stack
Size = 5 overflow
Push(1)
4 7
6
Push(5)
3
2 5 Push(6)
1 1
top 4
Push(7)
0

Stack Push(8)
Logical Representation of Stack
Size = 5 overflow
isFull()
4 7
6
true
3
2 5
1 1
top 0 4

Stack
Logical Representation of Stack
Size = 5 isEmpty()
4
true
3
2
1
top = -1 0

Stack
Implementation of Stack ADT –
Array and Linked List

2 ways of implementation
•Static memory allocation – arrays
•Dynamic memory allocation – linked
list
Implementation of Stack ADT -Array
push(value) - Inserting value into the stack
• Step 1 - Check whether stack is FULL. (top == SIZE-1)
• Step 2 - If it is FULL, then display "Stack is FULL!!! Insertion is not possible!!!" and terminate the function.
• Step 3 - If it is NOT FULL, then increment top value by one (top++) and set stack[top] to value (stack[top] = value).

void push(int value)


#define SIZE 10 {
if(top == SIZE-1)
int stack[SIZE], top = -1; printf("\nStack is Full!!! Insertion is not possible!!!");
else
{
top++;
stack[top] = value;
printf("\nInsertion success!!!");
}
}
Implementation of Stack ADT -Array
pop() - Delete a value from the Stack
• Step 1 - Check whether stack is EMPTY. (top == -1)
• Step 2 - If it is EMPTY, then display "Stack is EMPTY!!! Deletion is not possible!!!" and terminate the function.
• Step 3 - If it is NOT EMPTY, then delete stack[top] and decrement top value by one (top--).

void pop()
{
if(top == -1)
printf("\nStack is Empty!!! Deletion is not possible!!!");
else{
printf("\nDeleted : %d", stack[top]);
top--;
}
}
Implementation of Stack ADT -Array
display() - Displays the elements of a Stack
• Step 1 - Check whether stack is EMPTY. (top == -1)
• Step 2 - If it is EMPTY, then display "Stack is EMPTY!!!" and terminate the function.
• Step 3 - If it is NOT EMPTY, then define a variable 'i' and initialize with top. Display stack[i] value and decrement i value by one (i--).
• Step 3 - Repeat above step until i value becomes '0'.

void display(){
if(top == -1)
printf("\nStack is Empty!!!");
else{
int i;
printf("\nStack elements are:\n");
for(i=top; i>=0; i--)
printf("%d\n",stack[i]);
}
}
Implementation of Stack ADT –Linked List
• Every new element is inserted as 'top' element.
• Newly inserted element is pointed by 'top’.
• To remove an element from the stack, simply remove the node which is pointed
by 'top' by moving 'top' to its previous node in the list.
• The next field of the first element must be always NULL.
Implementation of Stack ADT –Linked List
push(value) - Inserting an element into the Stack
• Step 1 - Create a newNode with given value.
• Step 2 - Check whether stack is Empty (top == NULL)
• Step 3 - If it is Empty, then set newNode → next = NULL.
• Step 4 - If it is Not Empty, then set newNode → next = top.
• Step 5 - Finally, set top = newNode
void push(int value)
{
struct Node *newNode;
struct Node newNode = (struct Node*)malloc(sizeof(struct Node));
{ newNode->data = value;
int data; if(top == NULL)
struct Node *next; newNode->next = NULL;
}*top = NULL; else
newNode->next = top;
top = newNode;
}
Implementation of Stack ADT –Linked List
pop() - Deleting an Element from a Stack
• Step 1 - Check whether stack is Empty (top == NULL).
• Step 2 - If it is Empty, then display "Stack is Empty!!! Deletion is not possible!!!" and terminate the function
• Step 3 - If it is Not Empty, then define a Node pointer 'temp' and set it to 'top'.
• Step 4 - Then set 'top = top/temp → next'.
• Step 5 - Finally, delete 'temp'. (free(temp)).
void pop()
{
if(top == NULL)
printf("\nStack is Empty!!!\n");
else{
struct Node *temp = top;
printf("\nDeleted element: %d", temp->data);
top = temp->next;
free(temp);
}
}
Implementation of Stack ADT –Linked List
display() - Displaying stack of elements
• Step 1 - Check whether stack is Empty (top == NULL).
• Step 2 - If it is Empty, then display 'Stack is Empty!!!' and terminate the function.
• Step 3 - If it is Not Empty, then define a Node pointer 'temp' and initialize with top.
• Step 4 - Display 'temp → data --->' and move it to the next node. Repeat the same until temp reaches to the first node in the stack.
(temp → next != NULL).
• Step 5 - Finally! Display 'temp → data ---> NULL'. void display()
{
if(top == NULL)
printf("\nStack is Empty!!!\n");
else{
struct Node *temp = top;
while(temp->next != NULL){
printf("%d--->",temp->data);
temp = temp -> next;
}
printf("%d--->NULL",temp->data);
}
Applications of Stack
Reverse a string
Applications of Stack
Reverse a string
 abcd
d
c
b
a

Stack
Applications of Stack
Reverse a string
 abcd
d

c
b
a

Stack
Applications of Stack
Reverse a string
 abcd
dc

b
a

Stack
Applications of Stack
Reverse a string
 abcd
dcb

Stack
Applications of Stack
Reverse a string
 abcd
dcba

Stack
Applications of Stack
Reverse a string
Undo
Recursion
Checking balancing of parenthesis
Conversion of Infix to Postfix
Evaluation of Infix to Postfix
Applications of Stack in Data
Structure
Applications of Stack in Data Structure
• A Stack is a widely used linear data structure in modern computers in which
insertions and deletions of an element can occur only at one end, i.e., top of
the Stack. It is used in all those applications in which data must be stored
and retrieved in the last.

• An everyday analogy of a stack data structure is a stack of books on a desk,


Stack of plates, table tennis, Stack of bootless, Undo or Redo mechanism in
the Text Editors, etc.
Applications of Stack in Data Structure
• Infix to Postfix Conversion

• Postfix Evaluation

• Balancing symbols

• Function Calls

• Tower of Hanoi
Infix to Postfix Conversion
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.
8. 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.
9. At the end of the expression, pop and print all the operators of the stack.
Infix to Postfix Conversion
Rules for the conversion from infix to postfix expression
.
Infix to Postfix Conversion
Input Expression Stack Postfix Expression
K + L - M*N + (O^P) * W/U/V * T + Q
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 ^
Infix to Postfix Conversion
Input Expression Stack Postfix Expression
* +* 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+
Following is the various Applications of Stack in Data Structure:

• Evaluation of Arithmetic Expressions

• Backtracking

• Delimiter Checking

• Reverse a Data

• Processing Function Calls


Evaluation of Arithmetic Expressions

• A stack is a very effective data structure for evaluating arithmetic


expressions in programming languages. An arithmetic expression consists
of operands and operators.

• In addition to operands and operators, the arithmetic expression may also


include parenthesis like "left parenthesis" and "right parenthesis".

Example: A + (B - C)
Notations for Arithmetic Expression

• Infix Notation - The infix notation is a convenient way of writing an


expression in which each operator is placed between the operands.

Example: A + B, (C - D)

• Prefix Notation -The prefix notation places the operator before the operands.

Example: + A B, -CD

• Postfix Notation -The postfix notation places the operator after the operands.

Example: AB +, CD+
Infix Notation Prefix Notation Postfix Notation

A* B *AB AB*

(A+B)/C /+ ABC AB+C/

(A*B) + (D-C) +*AB - DC AB*DC-+


Backtracking

• Backtracking is another application of Stack. It is a recursive algorithm that


is used for solving the optimization problem.

Delimiter Checking

• The common application of Stack is delimiter checking, i.e., parsing that


involves analyzing a source program syntactically. It is also called
parenthesis checking. When the compiler translates a source program
written in some programming language such as C, C++ to a machine
language, it parses the program into multiple individual parts such as
variable names, keywords, etc. By scanning from left to right.
• The main problem encountered while translating is the unmatched delimiters. We
make use of different types of delimiters include the parenthesis checking (,),
curly braces {,} and square brackets [,], and common delimiters /* and */.

• Every opening delimiter must match a closing delimiter, i.e., every opening
parenthesis should be followed by a matching closing parenthesis. Also, the
delimiter can be nested. The opening delimiter that occurs later in the source
program should be closed before those occurring earlier.
Valid Delimiter Invalid Delimiter

While ( i > 0) While ( i >

/* Data Structure */ /* Data Structure

{ ( a + b) - c } { ( a + b) - c
• To perform a delimiter checking, the compiler makes use of a stack. When a
compiler translates a source program, it reads the characters one at a time, and if it
finds an opening delimiter it places it on a stack.

• When a closing delimiter is found, it pops up the opening delimiter from the top of
the Stack and matches it with the closing delimiter.
Reverse a Data

• To reverse a given set of data, we need to reorder the data so that the first
and last elements are exchanged, the second and second last element are
exchanged, and so on for all other elements.

• Example: Suppose we have a string Welcome, then on reversing it would


be Emoclew.
There are different reversing applications:

Reversing a string

A Stack can be used to reverse the characters of a string. This can be achieved
by simply pushing one by one each character onto the Stack, which later can
be popped from the Stack one by one. Because of the last in first
out property of the Stack, the first character of the Stack is on the bottom of
the Stack and the last character of the String is on the Top of the Stack and
after performing the pop operation in the Stack, the Stack returns the String in
Reverse order.
Converting Decimal to Binary:

• Although decimal numbers are used in most business applications, some


scientific and technical applications require numbers in either binary, octal,
or hexadecimal. A stack can be used to convert a number from decimal to
binary/octal/hexadecimal form.

• For converting any decimal number to a binary number, we repeatedly


divide the decimal number by two and push the remainder of each division
onto the Stack until the number is reduced to 0. Then we pop the whole
Stack and the result obtained is the binary equivalent of the given decimal
number.
• In the above example, on dividing 14 by 2, we get seven as a quotient and
one as the reminder, which is pushed on the Stack. On again dividing seven
by 2, we get three as quotient and 1 as the reminder, which is again pushed
onto the Stack. This process continues until the given number is not reduced
to 0. When we pop off the Stack completely, we get the equivalent binary
number 1110.
Processing Function Calls:

• Stack plays an important role in programs that call several functions in


succession. Suppose we have a program containing three functions: A, B, and
C. function A invokes function B, which invokes the function C.
• When we invoke function A, which contains a call to function B, then its
processing will not be completed until function B has completed its
execution and returned. Similarly for function B and C.

• So we observe that function A will only be completed after function B is


completed and function B will only be completed after function C is
completed.

• Therefore, function A is first to be started and last to be completed. To


conclude, the above function activity matches the last in first out behavior
and can easily be handled using Stack.
• Consider addrA, addrB, addrC be the addresses of the statements to which
control is returned after completing the function A, B, and C, respectively.
• The above figure shows that return addresses appear in the Stack in the
reverse order in which the functions were called. After each function is
completed, the pop operation is performed, and execution continues at the
address removed from the Stack.

• Thus the program that calls several functions in succession can be handled
optimally by the stack data structure. Control returns to each function at a
correct place, which is the reverse order of the calling sequence.
21CSC201J
DATA STRUCTURES AND
ALGORITHMS

UNIT-3
Topic : Infix to Postfix Conversion
Infix to Postfix Conversion

Infix Notation

• Infix Notation is the common arithmetic and logical formula notation.

• In which operators are written infix-style between the operands they act on.

Example: A + B
Infix to Postfix Conversion

Postfix Notation

• In Postfix Notation, the operators come after the operands.

• For example, the infix expression A + B will be written as AB+ in its Postfix
Notation.

• Postfix Notation is also called as “Reverse Polish Notation”


Infix to Postfix Conversion

Conversion from Infix to Postfix Algorithm

Step 1
• Scan the Infix expression from left to right for tokens (Operators, operands
and Parenthesis) and perform the steps 2 to 5 for each token in the expression.

Step 2

• If token is operand, Append it in postfix expression.

Step 3

• If token is left parenthesis “(“, push it in stack.


Infix to Postfix Conversion

Conversion from Infix to Postfix Algorithm

Step 4
If the token is an operator,

• Pop all the operators that are of higher or equal precedence than the
incoming token and append them (in the same order) to the output
expression.

• After popping out all such operators, push the new token on the stack.
Infix to Postfix Conversion

Conversion from Infix to Postfix Algorithm

Step 5
If “)” parenthesis is found,

• Pop all the operators from the stack and append them to the output string,
till you encounter the opening parenthesis “(“.

• Pop the left parenthesis but don’t append it to the output string (Postfix
notation does not have brackets).
Infix to Postfix Conversion

Conversion from Infix to Postfix Algorithm

Step 6
• When all tokens of infix expression have been scanned. Pop all the elements
from the stack and append them to the output string.

• The output string is the corresponding postfix notation.


Infix to Postfix Conversion

Example

Let the infix expression be:


A * (B + C) – D / E
Stage 1:
The stack is empty and we have only the infix expression
Infix to Postfix Conversion

Example

Let the infix expression be:


A * (B + C) – D / E
Stage 1:
The stack is empty and we have only the infix expression
Infix to Postfix Conversion

Example

Stage 2:
The first token is operand A. Operands are appended to the output as it is.
Infix to Postfix Conversion

Example

Stage 3:
Next token is * since stack is empty it is pushed into stack
Infix to Postfix Conversion

Example
Stage 4:
• Next token is ( the precedence of open parenthesis which is maximum.

• But when another operator is to come on the top of ‘(‘ then its precedence is
least.
Infix to Postfix Conversion

Example
Stage 5:
• Next token, B is an operand which will go to the output string
Infix to Postfix Conversion

Example
Stage 6:
• Next token, + is an operator, we consider the precedence of top element in the
stack. ‘(‘ . The outgoing precedence of open parenthesis is the least

• So + gets pushed into the stack.


Infix to Postfix Conversion

Example
Stage 7:
• Now token, C, is appended to the output
Infix to Postfix Conversion

Example
Stage 8:
• Now token, ) , means that pop all the elements from stack and append them to
the output till we read an opening parenthesis.
Infix to Postfix Conversion

Example
Stage 9:
• Now token, - , is an operator. The precedence of operator on the top of stack ‘*’
is more than that of ‘-’. So we pop multiply and append it to output. Then
push ‘– ‘ into stack.
Infix to Postfix Conversion

Example
Stage 10:
• Now token, D, is appended to the output
Infix to Postfix Conversion

Example
Stage 11:
• Next we insert the ‘/’ operator into the stack because its precedence is more
that the minus.
Infix to Postfix Conversion

Example
Stage 12:
• Now token, E, is appended to the output
Infix to Postfix Conversion

Example
Stage 13:
• The input expression is complete. So pop all the elements from stack and
append to the output.
Infix to Postfix Conversion
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.
Infix to Postfix Conversion
Rules for the conversion from infix to postfix expression
.
Infix to Postfix Conversion
Input Expression Stack Postfix Expression
K + L - M*N + (O^P) * W/U/V * T + Q
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 ^
Infix to Postfix Conversion
Input Expression Stack Postfix Expression
* +* 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+
Thank You
#include <limits.h> char peek() if(isFull()) {
#include <stdio.h> { printf("Stack Full!!!!"); switch (ch)
#include <stdlib.h> return stk[top]; {
#define MAX 20 } else{ case '+':
top++; case '-':
char stk[20]; char pop() stk[top] = oper; return 1;
int top = -1; { }
if(isEmpty()) } case '*':
int isEmpty() return -1; case '/':
{ int checkIfOperand(char ch) return 2;
return top == -1; char ch = stk[top]; {
} top--; return (ch >= 'a' && ch <= case '^':
'z') || (ch >= 'A' && ch <=
int isFull() return(ch); 'Z'); return 3;
{ } } }
return top == MAX - 1; return -1;
} void push(char oper) int precedence(char ch) }
{
int covertInfixToPostfix(char* expression) if (!isEmpty() && peek() != '(')
{ return -1;
int i, j; else
for (i = 0, j = -1; expression[i]; ++i) pop();
{ }
if (checkIfOperand(expression[i])) //1 else //4
expression[++j] = expression[i]; {
while (!isEmpty() &&
else if (expression[i] == '(') //2 precedence(expression[i]) <=
precedence(peek()))
push(expression[i]);
expression[++j] = pop();
push(expression[i]);
else if (expression[i] == ')') //3
}
{
while (!isEmpty() && peek() != '(')
}
expression[++j] = pop();
Thank You
21CSC201J

DATA STRUCTURES AND ALGORITHMS

UNIT-3

Topic : POSTFIX EVALUATION AND BALANCING SYMBOLS


Postfix evaluation
 Postfix notation (also known as Reverse Polish Notation) is a way to represent an expression,

where operators follow their corresponding operands. Evaluating an expression represented as

postfix notation can easily be done using the stack data structure.

 It is used in computers because it is faster than other types of notations (such as infix notation) as

parentheses are not required to represent them.

 Stack data structure is used to efficiently evaluate Postfix Expression.

 Time and Space complexity for evaluating postfix expression using stack are

 Time complexity - O(N)

 Space complexity -O(N)


Input-Output

 Input - A string consisting of numeric values along with operator symbols (*, /, +, -, etc) is given as the input.

 Output - The result obtained by solving the given input is required to be printed.

Examples of Postfix Expression Evaluation

Example 1 :

Input : 2 3 * 4 5 + * Output : 54

Example 2 :

Input : 5 2 3 * * Output : 30

Example 2 :

Input : 2 3 1 * + 9 - Output : -4
Algorithm for Evaluating Postfix Expression
Suppose P is an expression that we want to evaluate
1.Scan P from left to right and repeat steps 2 and 3 for each element pf P until null is encountered.
2.If an operand is encountered, put it on Stack.
3.If an operator © is encountered, then:
(a) Remove the two top elements of STACK, where A is the top element and B is the next to top
element.
(b) Evaluate B and A.
(c) Place the result on the STACK.
4.Result equal to the top element on STACK.
5.Exit.
At last, stack will consist of a single element .i.e. the result after evaluating the postfix expression.
Evaluating a Postfix Expression-Psuedocode
Opndstk = the empty stack;
/* scan the input string */
/* element at a time into symb */
While(not end of input)
symb = next imput character;
if(symb is an operand)
push(opnstk,symb);
else{
/* symb is an operator */
opnd2= pop(opndstk);
opnd1=pop(opndstk);
value=result of applying symb to opnd1 and opnd2;
push(opndstk,value);
} /* end else */
} /* end while */
Return(opndstk);
Suppose we are ask to solve below postfix expression 623+-382/+*2$3+

+ 49 3 52 52
Program to evaluate postfix expression
#include <stdio.h>
#include<math.h>
#define maxcols 80
#define true 1
#define false 0
double eval(char[]);
double pop(struct stack*);
void push(struct stack*, double);
int empty(struct stack*);
int isdigit(char);
double oper(int,double,double);
void main( ) {
char expr[maxcols];
int position =0;
while((expr[position++]=getchar())!=‘\n’);
expr[--position]=‘\0’;
printf(“\n The original postfix expression is “,expr);
printf(“\n%f”,eval(expr)); }
Program to evaluate postfix expression(Contd…)
else
struct stack{ {
int top; /* operator */
double items[maxcols]; opnd2= pop(&opndstk);
}; opnd1=pop(&opndstk);
double eval(char exp[]) value=oper(c,opnd1,opnd2);
{ push(&opndstk,value);
int c, position; }
double opnd1,opnd2,value; }
struct stack opndstk; return(pop(&opndstk));
opndstk=-1; } /* end eval */
for(position=0;(c=expr[position])!=‘\0’;position++)
{ int isdigit(char symb)
if(isdigi( c )) {
/* operand – convert the character representation */ return(symb>=‘0’ && symb<=‘9’);
/* of the digit into double and push it onto the stack */ }
push(&opndstk, (double)(c-’0’));
}
Program to evaluate postfix expression(Contd…)

double oper(int symb, double op1, double op2)


{
switch(symb)
{
case ‘+’ : return(op1+op2);
case ‘-’ : return(op1-op2);
case ‘*’ : return(op1*op2);
case ‘/’ : return(op1/op2);
case ‘$’ : return(pow(op1,op2));
default : printf(“ illegal operation”);
exit(1);
} /* switch */
} /* end oper */
Conclusion

 In postfix notation, the operator appears after the operands.


 It does not require the use of parentheses and therefore it is faster when
compared to other notations.
 Stack data structure is used for evaluation of postfix expression efficiently.
Balancing Symbols
Why Balancing Symbols?
 One of the most important applications of stacks is to check if the parentheses are balanced in a
given expression. The compiler generates an error if the parentheses are not matched.

 Here are some of the balanced and unbalanced expressions:

Consider the above mentioned unbalanced expressions:

 The first expression ( a + b is unbalanced as there is no closing parenthesis given.


 The second expression [ ( c - d * e ] is unbalanced as the closed round parenthesis is not given.
 The third expression { [ ( ] ) } is unbalanced as the nesting of square parenthesis and the round
parenthesis are incorrect.
Steps to find whether a given expression is balanced or
unbalanced
Steps to find whether a given expression is balanced or
unbalanced
Steps to find whether a given expression is balanced or
unbalanced
Code to find whether a given expression is balanced or
unbalanced
Code to find whether a given expression is balanced or
unbalanced
Steps to find whether a given expression is balanced or
unbalanced

 Input the expression and put it in a character stack.

 Scan the characters from the expression one by one.

 If the scanned character is a starting bracket ( ‘ ( ‘ or ‘ { ‘ or ‘ [ ‘), then push it to the stack.

 If the scanned character is a closing bracket ( ‘ ) ’ or ‘ } ’ or ‘ ] ’ ), then pop from the stack and if the

popped character is the equivalent opening bracket, then proceed. Else, the expression is

unbalanced.

 After scanning all the characters from the expression, if there is any parenthesis found in the stack or

if the stack is not empty, then the expression is unbalanced.

 Now, let us see a program to check balanced parentheses in the given expression.
Let's understand the above algorithm through an example.
Suppose expression is 2 * ( 6 + 5 )

Solution:
 First, the x variable is initialized by 0.
 The scanning starts from the variable '2', when it encounters '(' then the 'x' variable gets incremented by
1 and when the x reaches to the last symbol of the expression, i.e., ')' then the 'x' variable gets
decremented by 1 and it's final value becomes 0.
 We have learnt in the above algorithm that if x is equal to 0 means the expression is balanced;
 Therefore, the above expression is a balanced expression.
C Code
#include<stdio.h> void pop()
#include<stdlib.h> {
#include<string.h> if (s.top == - 1)
#define MAX 20 {
struct stack printf ("Stack is Empty\n");
{ }
char stk[MAX]; else
int top; {
}s; s.top = s.top - 1; // Pop the char and decrement top
void push(char item) }}
{
if (s.top == (MAX - 1)) int main(){
printf ("Stack is Full\n"); char exp[MAX];
else int i = 0;
{ s.top = -1;
s.top = s.top + 1; // Push the char and increment top printf("\nINPUT THE EXPRESSION : ");
s.stk[s.top] = item; scanf("%s", exp);
}}
C Code (Contd…)
for(i = 0;i < strlen(exp);i++)
{
if(exp[i] == '(' || exp[i] == '[' || exp[i] == '{')
{
push(exp[i]); // Push the open bracket
continue;
}
else if(exp[i] == ')' || exp[i] == ']' || exp[i] == '}')
// If a closed bracket is encountered
{
if(exp[i] == ')')
{
if(s.stk[s.top] == '(')
{
pop(); // Pop the stack until closed bracket is found
}
else
{
printf("\nUNBALANCED EXPRESSION\n");
break;
}}
21CSC201J

DATA STRUCTURES AND ALGORITHMS

UNIT-3

Topic : Function call & Towers of Hanoi


Applications of Stack: Function Call and
• Return
2 types of Memory
– Stack
– Heap
• Stack memory stores the data (variables) related to each function.
• Why is it called stack memory?
– The last function to be called is the first to return (LIFO)
– The data related to a function are pushed into the stack when a function is
called and popped when the function returns
Function Calls
• Function calls another function
->We are executing function A. In the course of its execution,
function A calls another function B. Function B in turn calls
another function C, which calls function D.
Function A
Function B
Function C
Function D
• When A calls B, A is pushed on top of the system stack. When the
execution of B is complete, the system control will remove A from the stack and
continue with its execution.
• When B calls C, B is pushed on top of the system stack. When the
execution of C is complete, the system control will remove B from the stack and
continue with its execution.
• When C calls D, C is pushed on top of the system stack. When the
execution of D is complete, the system control will remove C from the stack and
continue with its execution.
• When D calls E, D is pushed on top of the system stack.
When the
execution of E is complete, the system control will remove D from
the stack and continue with its execution.
• When E has executed, D will be removed for execution.
• When C has executed, B will be removed for execution.
• When D has executed, C will be removed for execution.
• When B has executed, A will be removed for execution.
Nested Function Calls
• Consider the code snippet below:
main() foo() bar()
{ { {
... ... ...
foo( bar( }
); );
...
bar( }
);
}
Nested Function Calls and Returns in Stack
Memory
• Stack memory when the code executes:

Direction of Stack growth


Image Source: https://loonytek.com/2015/04/28/call-stack-internals-part-1/
Function Call and Return in Stack Memory

• Each call to a function pushes the function’s activation record


(or stack frame) into the stack memory
• Activation record mainly consists of: local variables of the
function and function parameters
• When the function finishes execution and returns, its
activation record is popped
Recursion
• A recursive function is defined as a function that calls itself.
• Since a recursive function repeatedly calls itself, it makes use of the system
stack to temporarily store the return address and local variables of the calling
function.
• Every recursive solution has two major cases. They are
 Base case, in which the problem is simple enough to be solved directly without
making any further calls to the same function.
 Recursive case, in which first the problem at hand is divided into simpler sub-parts.
Second the function calls itself but with sub-parts of the problem obtained in the
first step. Third, the result is obtained by combining the solutions of simpler sub-
parts.
Recursion
• A recursive function is a function that calls itself during its
execution.
• Each call to a recursive function pushes a new activation record
(a.k.a stack frame) into the stack memory.
• Each new activation record will consist of freshly created local
variables and parameters for that specific function call.
• So, even though the same function is called multiple times, a new
memory space will be created for each call in the stack memory.
Recursion Handling by Stack Memory
• Factorial
program
int fact( int n)

Direction of Stack growth


{
if (n==1)
return 1; else
return
(n*fact(n-1));
}

fact(4);

Image Source: https://stackoverflow.com/questions/19865503/can-recursion-be-named-as-a-simple-function-call


Example : Factorial Of a Number
To calculate n!, we multiply the number with factorial of the number that is 1
less than that number.
n! = n * (n–1)!
5! = 5 * 4! Where 4! = 4 * 3!
= 5 * 4 * 3! Where 3! = 3 * 2!
= 5 * 4 * 3 * 2! Where 2! = 2*1!
= 5 * 4 * 3 * 2 * 1! Where 1! = 1
=5*4*3*2*1
= 120
Base case and Recursive Case
• Base case
When n = 1, because if n = 1, the result will be 1 as 1! = 1.
• Recursive case
Factorial function will call itself but with a smaller value of
n, this case can be given as
factorial(n) = n × factorial (n–1)
Advantages of using a recursive program
• Recursive solutions often tDestinationto be shorter and
simpler than non-recursive ones.
• Code is clearer and easier to use.
• Recursion works similar to the original formula to solve a
problem.
• Recursion follows a divide and conquer technique to
solve problems.
• In some (limited) instances, recursion may be more efficient.
Drawbacks/Disadvantages of using a
recursive program
• For some programmers and readers, recursion is a difficult concept.
• Recursion is implemented using system stack. If the stack space on the system
is limited,
• Recursion to a deeper level will be difficult to implement.
• Aborting a recursive program in midstream can be a very slow process.
• Using a recursive function takes more memory and time to execute as
compared to its nonrecursive counterpart.
• It is difficult to find bugs, particularly while using global variables.
Factorial using recursion
Towers of Hanoi

• Figure below shows three rings mounted on pole A.


• The problem is to move all these rings from pole A to pole C while maintaining the
same order. The main issue is that the smaller disk must always come above the
larger disk
• A-> Source pole
• B-> Spare pole
• C-> Destination pole
• To transfer all the three rings from A to C, we will first shift the upper
two rings (n–1 rings) from the source pole to the spare pole.
1) Source pole -> Destination pole
2) Source pole -> Spare pole
3) Destination pole -> Spare pole
• n–1 rings have been removed from pole A, the nth ring can be easily moved
from the source pole (A) to the destination pole (C).
4) Source pole -> Destination pole
• The final step is to move the n–1 rings from the spare pole (B) to
the destination pole (C).
5) Spare pole -> Source pole
6) Spare pole -> Destination pole
7) Source pole -> Destination pole
Base case: if n=1
Move the ring from A to C using B as spare
Recursive case:
Move n – 1 rings from A to B using C as spare
Move the one ring left on A to C using B as spare
Move n – 1 rings from B to C using A as spare
Applications of Recursion: Towers of Hanoi
Towers of Hanoi Problem:
There are 3 pegs and n disks. All the n disks are stacked in 1 peg in the order of their
sizes with the largest disk at the bottom and smallest on top. All the disks have to be
transferred to another peg.
Rules for transfer:
• Only one disk can be moved at a time.
• Each move consists of taking the upper disk from one of the stacks and placing it
on top of another stack i.e. a disk can only be moved if it is the uppermost disk
on a stack.
• No disk may be placed on top of a smaller disk.
Towers of Hanoi
• Initial Position:

A B c

• All disks from peg A have to be transferred to peg C


Towers of Hanoi Solution for 3 disks
Towers of Hanoi Solution for 3 disks
Towers of Hanoi – Recursive Solution
/* N = Number of disks
Source, Aux, Destination are the pegs */

Tower(N, Source, Aux, Destination)


Sourcein
if N = 1 then
Print: Source --> Destination;
else
Call Tower(N-1, Source, Destination,Aux);
Print: Source --> Destination;
Call Tower(N-1, Aux, Source,Destination);
Destinationif
Destination
Code Snippet
• Function Call:
move(n,'A', 'C', 'B');
• Function Definition:
21CSC201J
DATA STRUCTURES AND
ALGORITHMS

UNIT-3
Topic : Introduction to Queue,
Operations on Queue ADT - Create,
Enqueue and Dequeue
Introduction to Queue
• Queue is a linear data structure in which the insertion and deletion operations are performed
at two different ends.
• In queue data structure, the insertion and deletion operations are performed based on FIFO
(First In First Out) principle.
• The insertion is performed at one end and deletion is performed at another end.
• In a queue data structure, the insertion operation is performed at a position which is known
as 'rear' and the deletion operation is performed at a position which is known as 'front'.
Introduction to Queue
• The representation of the queue is shown in the below image -
Introduction to Queue
Let us explain the concept of queues using the analogies given below.
• People moving on an escalator. The people who got on the escalator first
will be the first one to step out of it.
• People waiting for a bus. The first person standing in the line will be the
first one to get into the bus.
• People standing outside the ticketing window of a cinema hall. The first
person in the line will get the ticket first and thus will be the first one to
move out of it.
• Luggage kept on conveyor belts. The bag which was placed first will be the
first to come out at the other end.
• Cars lined at a toll bridge. The first car to reach the bridge will be the first
to leave.
Introduction to Queue
• Conceptual View of a Queue
Types of Queue
Types 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.
Types of 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.
Types of 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 -
Types of Queue
Priority Queue

• 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.
Types of 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.
• The representation of the deque is shown in the below image -
Operations on a Queue

Enqueue The Enqueue operation is used to


insert the element at the rear end of
the queue.
Dequeue It performs the deletion from the
front-end of the queue.
Peek or First Examines the element at the front of
the queue
Queue underflow (isEmpty) It shows the underflow condition
when the Queue is empty, i.e., no
elements are in the Queue.
Queue overflow (isfull) It shows the overflow condition when
the queue is completely full.
Operations on a Queue
The enqueue and dequeue is given below in a stepwise manner.

Enqueue :
• Step 1: Check if the queue is full or not by comparing the number of elements in the
queue with the maximum size of the queue.
• Step 2: If the queue is full, then display an overflow message and return.
• Step 3: If the queue is not full, then increment the rear pointer and insert the new
element at the position pointed by the rear pointer.

Dequeue :
• Step 1: Check if the queue is empty or not by comparing the number of elements in
the queue with 0.
• Step 2: If the queue is empty, then display an underflow message and end the
program.
• Step 3: If the queue is not empty, then remove the element at the front of the queue
and increment the front pointer.
Ways to implement the Queue ADT

There are two ways of implementing the Queue:


• Implementation using array: The sequential allocation in a Queue can
be implemented using an array.
• Implementation using Linked list: The linked list allocation in a Queue
can be implemented using a linked list.
21CSC201J
DATA STRUCTURES AND
ALGORITHMS

UNIT-3
Topic : Queue Implementation
Ways to implement the Queue ADT

There are two ways of implementing the Queue:


• Implementation using array: The sequential allocation in a Queue can be
implemented using an array.
• Implementation using Linked list: The linked list allocation in a Queue can be
implemented using a linked list.
Array implementation of Queue

ARRAY REPRESENTATION OF QUEUES:


Array is the easiest way to implement a queue. Queue can be also implemented
using Linked List or Stack.
Queues can be easily represented using linear arrays. As stated earlier, every queue
has FRONT and REAR variables that point to the position from where deletions and
insertions can be done, respectively.
Array implementation of Queue

• Queue can be implemented using a one- dimensional array.


• We need to keep track of 2 variables: front
• and rear
• front is the index in which the first element is present
• rear is the index in which the last element is present
Array implementation of Queue

Algorithm to insert an element in a queue Algorithm to delete an element from a queue


Step 1: IF REAR = MAX-1 Step 1: IF FRONT = -1 OR FRONT > REAR
Write OVERFLOW Goto step 4 Write UNDERFLOW
[END OF IF] ELSE
Step 2: IF FRONT = -1 and REAR = -1 SET VAL = QUEUE[FRONT]
SET FRONT = REAR=0 SET FRONT =FRONT + 1
ELSE [END OF IF]
SET REAR = REAR + 1 Step 2: EXIT
[END OF IF]
Step 3: SET QUEUE[REAR] = NUM
Step 4: EXIT
.
Array implementation of Queue
Code to insert an element in a queue if(front == -1 && rear == -1)
int front = -1, rear = -1; {
int queue[maxsize]; front = 0;
void insert() rear = 0;
{ }
int item; else
printf("\nEnter the element\n"); {
scanf("\n%d",&item); rear = rear+1;
if(rear == maxsize-1) }
{ queue[rear] = item;
printf("\nOVERFLOW\n"); printf("\nValue inserted ");
.
return;
}
Array implementation of Queue
Code to delete an element in a queue if(front == rear)
void delete() {
{ front = -1;
int item; rear = -1 ;
if (front == -1 || front > rear) }
{ else
printf("\nUNDERFLOW\n"); {
return; front = front + 1;
}
} printf("\nvalue deleted ");
else } //else
{
item = queue[front]; } //delete
.
Array implementation of Queue
Code to display queue items
else
{
void display() printf("\nprinting values .....\n");
{ for(i=front;i<=rear;i++)
int i;
if(rear == -1) printf("\n%d\n",queue[i]);
{
printf("\nEmpty queue\n"); } //else
}

} // display
.
Disadvantage of Array Implementation of Queue

• Multiple enqueue and dequeue operations may lead to situation


like this:

• No more insertion is possible (since rear cannot be incremented anymore) even


though 2 spaces are free.
• Workaround: Circular Queue, Shifting elements after each dequeue
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.

• Deciding the array size: One 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.
LINKED LIST REPRESENTATION OF QUEUES

• Due to the drawbacks discussed in the previous section, the array implementation can
not be used for the large scale applications where the queues are implemented. One of
the alternative of array implementation is linked list implementation of queue.
• The storage requirement of linked representation of a queue with n elements is o(n)
while the time requirement for operations is o(1).
• In a linked queue, each node of the queue consists of two parts i.e. data part and the
link part. Each element of the queue points to its immediate next element in the
memory.
• In the linked queue, there are two pointers maintained in the memory i.e. front pointer
and rear pointer. The front pointer contains the address of the starting element of the
queue while the rear pointer contains the address of the last element of the queue.
• Insertion and deletions are performed at rear and front end respectively. If front and
rear both are NULL, it indicates that the queue is empty.
LINKED LIST REPRESENTATION OF QUEUES

.
Linked List Implementation of Queue
• In the linked list implementation of queue, two pointers are used, namely, front
and rear
• New nodes are created when an element has to be inserted.
• Each node consists of queue element and a pointer to the next node
Linked List implementation of Queue

An element in a queue
struct node
{
int data;
struct node *next;
};
struct node *front;
struct node *rear;
.
Linked List implementation of Queue
Code to insert an element in a queue if(front == NULL)
void insert() {
{ front = new;
rear = new;
struct node *new;
//front -> next = NULL;
int item; //rear -> next = NULL;
new = (struct node *) malloc (sizeof(struct node)); }
if(new == NULL) else
{ printf("\nOVERFLOW\n"); return; } {
rear -> next = new;
else
rear = new;
{ //rear->next = NULL;
printf("\nEnter value?\n"); }
scanf("%d",&item); } //else
new -> data = item; }
new-> next = NULL;
Linked List implementation of Queue
void delete () void display()
{ {
struct node *temp; struct node *ptr;
ptr = front;
if(front == NULL)
if(front == NULL)
{ {
printf("\nUNDERFLOW\n"); printf("\nEmpty queue\n");
return; }
} else
{ printf("\nprinting values .....\n");
else
while(ptr != NULL)
{ {
temp = front; printf("\n%d\n",ptr -> data);
front = front -> next; ptr = ptr -> next;
free(temp); }
}
}
}
CIRCULAR QUEUES
Circular 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.
CIRCULAR QUEUES
• Circular Queue is a variation of the
traditional linear queue, where the
last element connects back to the
first element, forming a circular
structure.
• This ensures continuous use of
memory and efficient space
utilization.
• The front and rear pointers wrap
around the array, allowing us to
enqueue elements at the rear and
dequeue elements from the front
while keeping the logical order
intact.
Need for Circular Queue
• In a standard linear queue implemented using arrays, there is a limitation in
reusing the vacant spaces left after dequeueing elements.

• As elements are dequeued from the front, the queue's front pointer moves
forward, leaving unused memory space at the beginning. This inefficient memory
usage can be overcome with the Circular Queue.
Disadvantage of Linear Queue

• Multiple enqueue and dequeue operations may lead to situation


like this:

• No more insertion is possible (since rear cannot be incremented anymore) even


though 2 spaces are free.
• Workaround: Circular Queue, Shifting elements after each dequeue
Circular Queue Operations
• Enqueue: Adding an element to the rear of the queue.
• Dequeue: Removing an element from the front of the queue.
• Front: Accessing the front element of the queue without removing it.
• Rear: Accessing the rear element of the queue without removing it.
• IsEmpty: Checking if the queue is empty.
• IsFull: Checking if the queue is full.
Circular Queue Implementation
CircularQueue:
max_size // maximum size of the Circular Queue
queue // an array to hold the elements of the Circular Queue
front // pointer to the front of the Circular Queue
rear // pointer to the rear of the Circular Queue
Constructor to initialize the Circular Queue
CircularQueue(max_size):
max_size = max_size
queue = [None] * max_size
front = rear = -1
Function to check if the Circular Queue is empty
is_empty():
return front == -1
Function to check if the Circular Queue is full
is_full():
return (rear + 1) % max_size == front
Enqueue
enqueue(data):
if is_full():
// The Circular Queue is full, enqueue operation cannot be performed
return "Queue is full"
if is_empty():
// If the Circular Queue is empty, set both front and rear to 0
front = rear = 0
else:
// Increment the rear pointer in a circular manner
rear = (rear + 1) % max_size
// Add the new element to the rear of the Circular Queue
queue[rear] = data
return "Enqueued element"
Dequeue
dequeue():
if is_empty():
// The Circular Queue is empty, dequeue operation cannot be performed
return "Queue is empty"
data_to_dequeue = queue[front]
if front == rear:
// If there is only one element, reset front and rear to -1 to indicate an empty queue
front = rear = -1
else:
// Increment the front pointer in a circular manner
front = (front + 1) % max_size
return data_to_dequeue
Front element of the Circular Queue
peek_front():
if is_empty():
// The Circular Queue is empty, no front element to peek
return "Queue is empty"
return queue[front]
Rear element of the Circular Queue
peek_rear():
if is_empty():
// The Circular Queue is empty, no rear element to peek
return "Queue is empty"
return queue[rear]
Advantages of Circular Queue
1. Efficient Memory Utilization: Circular Queues overcome the
issue of wasted space by reusing the vacant slots.
2. Simplicity in Implementation: They can be implemented using
arrays, which are contiguous blocks of memory.
3. Continuous Data Flow: Circular Queues allow continuous data
flow without the need for resetting the front pointer.
Applications of Circular Queues
• Printer Spooling: Managing print jobs in a printer queue.
• CPU Task Scheduling: Scheduling tasks in operating systems.
• Buffer Management in Networking: Handling data packets in
network routers.
Review Questions
1. Explain the concept of a circular queue? How is it better than a linear queue?
2. Draw the queue structure in each case when the following operations are performed on an empty queue.
(a)Add A, B, C, D, E, F. (b) Delete two letters
(c) Add G (d) Add H
(e) Delete four letters (f) Add I
3. The circular queue will be full only when _____.
(a) FRONT=MAX-1 and REAR=MAX-1 (b) FRONT=0 and REAR=MAX-1
(c) FRONT=MAX-1 and REAR=0. (d) FRONT=0 and REAR=0
4. The function that deletes values from a queue is called ______.
(a) Enqueue (b)dequeue
(c) Pop (d) peek
5. New nodes are added at of the queue.
6. _________ allows insertion of elements at either ends but not in the middle.
7. A queue is a _____ data structure in which the element that is inserted first is the first one to be taken out.
8. In a circular queue, the first index comes after the ______ index.
9. In the computer’s memory, queues can be implemented using _________ and _______.
10. The storage requirement of linked representation of queue with n elements is ____ and the typical time
requirement for operations is _______.
21CSC201J_Data Structures And Algorithms
QUEUES
• A queue is a data structure that models/enforces the first-come first-serve order, or
equivalently the first-in first-out (FIFO) order.

• A waiting line is a good real-life example of a queue. (In fact, the British word for
“line” is “queue”.)
Double Ended 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.
• The representation of the deque is shown in the below image -
DEQUES (Double Ended Queue)
• Elements can be added or removed at either ends.
• It is a generalized version of the linear queue in which both the insertion and
deletion can be performed at both the ends.
Properties of Deque
• A Deque can perform both the Insertion and Deletion based on the Last in First
Out (LIFO) principle.
Properties of Deque
• A Deque can also perform the Insertion and Deletion based on First in First Out
(FIFO) principle.
Types of Deque

❑ Input restricted Deque

❑ Output restricted Deque


Input restricted Deque

❑ Input restricted Deque


• It is Deque that has certain restrictions for inserting an element.
• Elements can be inserted from one end.
• Elements can be removed at both the ends.
Output restricted Deque

Output restricted Deque


• It is Deque that has certain restrictions for removing an element from deque.
• Elements can be removed from one end.
• Elements can be inserted at both the ends.
Operations in Deque

• The basic operations that can be performed on Deque are


1. Insertion at front
2. Insertion at rear
3. Deletion at front
4. Deletion at rear
Double Ended Queue
• Operations performed on deque:
• Insertion at front
• Insertion at rear
• Deletion at front
• Deletion at rear
Double Ended Queue
• Declare an Array of size N. This array will be used as a deque
• Set 2 pointers at the first position where Front = -1 and Rear = 0
Double Ended Queue
Check if Deque is Empty or Not Empty
If front = -1, then deque is empty else it’s not empty.

Check if Deque is Full or Not Full


If front == 0 and rear == n-1 , then deque is Full

->If front == rear + 1 , then also deque is Full.

->If the deque is Full ,then any new element cannot be inserted.
Double Ended Queue
• Insert at the Front
• Algorithm:
• Check the position of the
front
• If front < 1 , reinitialize
front = N-1 ( last index )
• Then add new element to
Array[front]
Double Ended Queue
void insert_front(int add_item)
{
if((front == 0 && rear == Size-1) || (front ==
rear+1))
{ printf("Queue Overflow \n");
return; }
if (front == -1)/*If queue is initially empty*/
{ front = 0;
rear = 0; }
else
if(front== 0)
front=Size-1;
else
front=front-1;
deque_arr[front] = added_item ;
}
Insertion at Front
Double Ended Queue
• Insert at Rear
• Algorithm:
• Check if the array is full or not
• If it’s full , reinitialize rear = 0
else increase rear by 1.
• Then add the element to
Array[rear]
Double Ended Queue
void insert_rear(int added_item)
{
if((front == 0 && rear == Size-1) || (front == rear+1))
{ printf("Queue Overflow\n");
return;}
if (front == -1) /* if queue is initially empty */
{ front = 0;
rear = 0;}
else
if(rear == Size-1) /*rear is at last position of queue */
rear = 0;
else
rear = rear+1;
deque_arr[rear] = added_item ;
}
Insertion at Rear
Double Ended Queue
• Delete from Front
• Algorithm:
• Check if deque is Empty or not Empty.
• If deque is Empty, then deletion operation
cannot be performed . In this case , we will
simply print UNDERFLOW. In this condition ,
the front will be -1.
• If deque has only 1 element , then only 1
operation of deletion is possible. In case of 1
element , front == rear.
• We will set front =-1 and rear = -1
• If the front is at the last index
(front == n-1 ) , set front = 0.
• Else set Front = Front + 1.
Double Ended Queue
void delete_front()
{ if (front == -1)
{ printf("Queue Underflow\n");
return ;
}
printf("Element deleted from queue is :
%d\n",deque_arr[front]);
if(front == rear) /*Queue has only one element */
{ front = -1;
rear=-1;
}
else
if(front == Size-1)
front = 0;
else
front = front+1;
}
Delete at front
Double Ended Queue
• Delete from Rear
• Algorithm:
• Check if the deque is empty or not
empty. If it’s empty, then we cannot
perform a deletion operation. We will
directly print UNDERFLOW.
• If the deque has only 1 element i.e
front==rear, we will set front = -1 and
rear =-1
• If rear is at 0 , then set rear = n-1
• Else rear = rear-1
Double Ended Queue
void delete_rear()
{
if (front == -1)
{
printf("Queue Underflow\n");
return ;
}
printf("Element deleted from queue is : %d\n",deque_arr[rear]);
if(front == rear) /*queue has only one element*/
{
front = -1;
rear=-1;
}
else
if(rear == 0)
rear=Size-1;
else
rear=rear-1; }
Delete at rear
APPLICATIONS OF DEQUE

Palindrome-checker
APPLICATIONS OF DEQUE

A-Steal job scheduling algorithm

• The A-Steal algorithm implements task scheduling for several processors(multiprocessor scheduling).
• The processor gets the first element from the deque.
• When one of the processor completes execution of its own threads it can steal a thread from
another processor.

• It gets the last element from the deque of another processor and executes it.
OTHER APPLICATIONS OF DEQUE

• Undo-Redo operations in Software applications.


• Storing a web browser's history. Recently visited URLs are added to the front of the
deque, and the URL at the back of the deque is removed after some specified
number of insertions at the front.
21CSC201J
DATA STRUCTURES AND
ALGORITHMS

UNIT-4
Topic : Trees
Classification of Data Structures
Introduction to Trees
• The study of trees in mathematics can be traced to Gustav Kirchhoff in the
middle nineteenth century and several years later to Arthur Cayley, who
used trees to study the structure of algebraic formulas.
• Cayley’s work undoubtedly laid the framework for Grace Hopper’s use of
trees in 1951 to represent arithmetic expressions.
• Hopper’s work bears a strong resemblance to today’s binary tree formats.
• Trees are used extensively in computer science to represent algebraic
formulas;
• as an efficient method for searching large, dynamic lists; and for such
diverse applications as artificial intelligence systems and encoding
algorithms.
General Trees
• Linear access time for linked lists too high.
• Solution – group data into trees.
• Trees – non linear data structure
• Used to represent data contains a hierarchical relationship among
elements example: family, record
• Worst Case time – O(log n)
• Tree can be defined in many ways, eg: recursively
General Trees
• A tree is a non-linear data structure.
• Tree is a non-linear data structure which organizes data in
hierarchical structure and this is a recursive definition.
• A tree is a collection of nodes connected by directed (or
undirected) edges.
• A tree can be empty with no nodes or a tree is a structure
consisting of one node called the root and zero or one or more
subtrees.
What is tree data structure ?
• The tree is a non linear data structure that consist of nodes and is connected by
edges.
• A tree data structure is a hierarchical structure that is used to represent and
organize data in a way that is easy to navigate and search.
• It is a collection of nodes that are connected by edges and has a hierarchical
relationship between the nodes.

• The topmost node of the tree is called the root,


and the nodes below it are called the child nodes.
Each node can have multiple child nodes, and these
child nodes can also have their own child nodes,
forming a recursive structure.
Basic Terminologies In Tree Data Structure:
Tree Terminology
Root Height
Edge Depth
Parent Subtree
Child Forest
Sibling Path
Degree Ancestor
Internal node Descendant
Leaf node
Level
Tree Terminology - Root
• First node is called as Root Node.
• Every tree must have a root node.
• The root node is the origin of the tree data structure.
• In any tree, there must be only one root node. We never have
multiple root nodes in a tree.
Tree Terminology - Root
• The stating node of a tree is root node
• Only one root node
Tree Terminology - Edge
• the connecting link between any two nodes is called as EDGE.
• In a tree with 'N' number of nodes there will be a maximum of 'N-1'
number of edges.
Tree Terminology - Edge
• Nodes are connected using the link called edges
• Tree with n nodes exactly have (n-1) edges
Tree Terminology - Parent
• A predecessor of any node is called as PARENT NODE.
• The node which has a branch from it to any other node is called a parent
node.
• Parent node can also be defined as "The node which has child / children".
Tree Terminology - Parent
• Node that have children nodes or have branches connecting to other
nodes
• Parental node have one or more children nodes
Tree Terminology - Child
• Node that is descendant of any node is child
• All nodes except root node are child node
• Any parent node can have any number of child nodes.
Tree Terminology - Child
Tree Terminology - Siblings
• Nodes with same parents are siblings
Tree Terminology - Siblings
Tree Terminology - Degree
• Degree of node – number of children per node
• Degree of tree – highest degree of a node among all nodes in tree

Degree A – 2
Degree B – 3
Degree C – 0
Degree D – 0
Degree E – 0
Degree F – 0
Degree of entire tree - 3
Tree Terminology - Degree
• Total number of children of a node is called as DEGREE of that Node
Tree Terminology – Internal Node
• Node with minimum one child – internal node
• Also known as non – terminal nodes
Tree Terminology – Internal Node
Tree Terminology – Leaf Node
• Node with no child – Leaf node
• Also known as External nodes or Terminal nodes
Tree Terminology – Leaf Node
• the node which does not have a child is called as LEAF Node
Tree Terminology – Level
• Each step of tree is level number
• Staring with root as 0
• the root node is said to be at Level 0 and the children of root node
are at Level 1
Tree Terminology – Height
• Number of edges in longest path from the node to any leaf
• Height of any leaf node is 0
• Height of Tree = Height of Root node

Height A – 3
Height B – 2
Height D – 1
Height C,G,E,F – 0
Height of tree - 3
Tree Terminology – Height
• total number of edges from leaf node to a particular node in the
longest path
Tree Terminology – Depth
• Number of edges from root node to particular node is Depth
• Depth of root node is 0
• Depth of Tree = Depth of longest path from root to leaf

Depth A – 0
Depth B ,C – 1
Depth D, E, F – 2
Depth G – 3
Depth of tree - 3
Tree Terminology – Depth
• total number of edges from root node to a particular node
Tree Terminology – Subtree
• Each child of a tree forms a subtree recursively
Tree Terminology – Forest
• Set of disjoint trees
Tree Terminology - Path
• The sequence of Nodes and Edges from one node to another
node is called as PATH between that two Nodes.
• Length of a Path is total number of nodes in that path.
Tree Terminology - Path
• A path from node a1 to ak is defined as a sequence of nodes a1, a2, . . .
, ak such that ai is the parent of ai+1 for 1 ≤ i < k

Path A-G: A,B,D,G


Tree Terminology – length of Path
• Number of edges in the path

Path A-G: A – B – D – G

Length of path A-G : 3


Tree Terminology – Ancestor & Descendant
• If there is a path from A to B, then A is an ancestor of B and B is a
descendant of A

Path A-G: A – B – D – G

A – Ancestor for G, B, D…

G – Descendant of D,B,A
Tree Representations
A tree data structure can be represented in two methods.
• List Representation
• Left Child - Right Sibling Representation
Tree Representations
List Representation
1. Two types of nodes one for representing the node with data
called 'data node' and another for representing only references
called 'reference node'.
2. start with a 'data node' from the root node in the tree. Then it
is linked to an internal node through a 'reference node' which is
further linked to any other node directly
Tree Representations
• List Representation
Tree Representations
Left Child - Right Sibling Representation
• A list with one type of node which consists of three fields namely
Data field, Left child reference field and Right sibling reference field.
• Data field stores the actual value of a node
• left reference field -> address of the left child
• right reference field -> address of the right sibling node.
Tree Representations
Left Child - Right Sibling Representation
• In this representation, every node's data field stores the actual value
of that node.
• If that node has left a child, then left reference field stores the
address of that left child node otherwise stores NULL.
• If that node has the right sibling, then right reference field stores the
address of right sibling node otherwise stores NULL.
Tree Representations
Left Child - Right Sibling Representation
Tree Representations
Left Child - Right Sibling Representation
Binary Tree
• A tree in which every node can have a maximum of two children is
called Binary Tree.
• In a binary tree, every node can have either 0 children or 1 child or 2
children but not more than 2 children.
Binary Tree
• A binary tree is a data structure specified as a set of node elements.
• The topmost element in a binary tree is called the root node, and each
node has 0, 1, or at most 2 kids.
Binary Tree
Properties of Binary Trees
• At each level of n, the maximum number of nodes is 2n
• Maximum number of nodes possible at height h is 2h+1 -1
• The minimum number of nodes possible at height h is h+1
Strict Binary Tree
• Each node must contain either 0 or 2 children.
• Also a tree in which each node must contain 2 children except the leaf
nodes. All nodes filled from left to right
Strict Binary Tree / full Binary tree
• A full Binary tree is a special type of binary tree in which every parent
node/internal node has either two or no children.
Complete Binary Tree
• Except last level, all the nodes need to completely filled
• All nodes filled from left to right
Complete Binary Tree
• The complete binary tree is a tree in which all the nodes are completely
filled except the last level.
• In the last level, all the nodes must be as left as possible. The nodes
should be added from the left.
• All nodes filled from left to right
Representation of Binary Tree
• Each node – three portion – Data portion, left child pointer, Right
child pointer
Linked List Representation of Tree
struct node
{
struct node *left;
int value;
struct node *right;
};
Linked List Representation of Tree
struct node printf("\nPress 1 for new node");
{ printf("Enter your choice : ");
int data; scanf("%d", &choice);
struct node *left, *right; if(choice==0)
} { return 0; }
void main() else
{ {
struct node *root; printf("Enter the data:");
root = create(); scanf("%d", &data);
} temp->data = data;
struct node *create() printf("Enter the left child of %d", data);
{ temp->left = create();
struct node *temp; printf("Enter the right child of %d", data);
int data; temp->right = create();
temp = (struct node *)malloc(sizeof(struct node)); return temp;
printf("Press 0 to exit"); } }
Linked List Representation of Tree
21CSC201J
DATA STRUCTURES AND
ALGORITHMS

UNIT-4
Topic : Tree Traversal
Tree Traversal
• Traversal – visiting all node only once
• Based on the order of visiting :
• In – order traversal
• Pre – order traversal
• Post – order traversal
Tree traversal
• The term 'tree traversal' means traversing or visiting each node of a
tree. Traversing can be performed in three ways
Pre-order traversal
In-order traversal
Post-order traversal
Pre-order traversal
Algorithm
Step 1 - Visit the root node
Step 2 - Traverse the left subtree recursively.
Step 3 - Traverse the right subtree recursively.
The output of the preorder traversal of the above tree is -
A→B→D→E→C→F→G
Tree TRAVERSAL

In-order traversal
• The first left subtree is visited after that root node is traversed, and
finally, the right subtree is traversed.
Algorithm
Step 1 - Traverse the left subtree recursively.
Step 2 - Visit the root node.
Step 3 - Traverse the right subtree recursively.

The output of the inorder traversal of the above tree is -


D → B → E →A→ F→ C →G
Tree TRAVERSAL
Post-order traversal
the first left subtree of the root node is traversed, after that recursively
traverses the right subtree, and finally, the root node is traversed.
Algorithm
Step 1 - Traverse the left subtree recursively.
Step 2 - Traverse the right subtree recursively.
Step 3 - Visit the root node.

The output of the postorder traversal of the above tree is -


D→E→B→F→G→C→A
Tree Traversal In detail
In-order Traversal
• Traverse left node , visit root node, Traverse right node

Traverse Left node– B


No left child for B subtree
Visit root node B
No right child for B subtree
Visit root node A
B–A–C Traverse Right node– C
No left child for C subtree
Visit root node C
No right child for C subtree
In-order Traversal
Algorithm:
Inorder(Tree)
1. while Tree != Null then Repeat step 2 – 4
2. Inorder(Tree->left)
3. Write(Tree->Data) // root
4. Inorder(Tree->right)
5. End
In-order Traversal
Algorithm: Inorder(Tree)
1. Repeat step 2 – 4 while Tree != Null
2. Inorder(Tree->left)
3. Write(Tree->Data)
4. Inorder(Tree->right)
5. End
In-order Traversal
Algorithm: Inorder(Tree)
1. Repeat step 2 – 4 while Tree != Null
2. Inorder(Tree->left)
3. Write(Tree->Data)
4. Inorder(Tree->right)
5. End
In-order Traversal
Algorithm: Inorder(Tree)
1. Repeat step 2 – 4 while Tree != Null
2. Inorder(Tree->left)
3. Write(Tree->Data)
4. Inorder(Tree->right)
5. End
In-order Traversal
Algorithm: Inorder(Tree)
1. Repeat step 2 – 4 while Tree != Null
2. Inorder(Tree->left)
3. Write(Tree->Data)
4. Inorder(Tree->right)
5. End
In-order Traversal
Algorithm: Inorder(Tree)
1. Repeat step 2 – 4 while Tree != Null
2. Inorder(Tree->left)
3. Write(Tree->Data)
4. Inorder(Tree->right)
5. End
Result : G,
In-order Traversal
Algorithm: Inorder(Tree)
1. Repeat step 2 – 4 while Tree != Null
2. Inorder(Tree->left)
3. Write(Tree->Data)
4. Inorder(Tree->right)
5. End
Result : G, D,
In-order Traversal
Algorithm: Inorder(Tree)
1. Repeat step 2 – 4 while Tree != Null
2. Inorder(Tree->left)
3. Write(Tree->Data)
4. Inorder(Tree->right)
5. End
Result : G, D,
In-order Traversal
Algorithm: Inorder(Tree)
1. Repeat step 2 – 4 while Tree != Null
2. Inorder(Tree->left)
3. Write(Tree->Data)
4. Inorder(Tree->right)
5. End
Result : G, D, H,
In-order Traversal
Algorithm: Inorder(Tree)
1. Repeat step 2 – 4 while Tree != Null
2. Inorder(Tree->left)
3. Write(Tree->Data)
4. Inorder(Tree->right)
5. End
Result : G, D, H, L,
In-order Traversal
Algorithm: Inorder(Tree)
1. Repeat step 2 – 4 while Tree != Null
2. Inorder(Tree->left)
3. Write(Tree->Data)
4. Inorder(Tree->right)
5. End
Result : G, D, H, L, B
In-order Traversal
Algorithm: Inorder(Tree)
1. Repeat step 2 – 4 while Tree != Null
2. Inorder(Tree->left)
3. Write(Tree->Data)
4. Inorder(Tree->right)
5. End
Result : G, D, H, L, B, E
In-order Traversal
Algorithm: Inorder(Tree)
1. Repeat step 2 – 4 while Tree != Null
2. Inorder(Tree->left)
3. Write(Tree->Data)
4. Inorder(Tree->right)
5. End
Result : G, D, H, L, B, E, A,
In-order Traversal
Algorithm: Inorder(Tree)
1. Repeat step 2 – 4 while Tree != Null
2. Inorder(Tree->left)
3. Write(Tree->Data)
4. Inorder(Tree->right)
5. End
Result : G, D, H, L, B, E, A,
In-order Traversal
Algorithm: Inorder(Tree)
1. Repeat step 2 – 4 while Tree != Null
2. Inorder(Tree->left)
3. Write(Tree->Data)
4. Inorder(Tree->right)
5. End
Result : G, D, H, L, B, E, A, C
In-order Traversal
Algorithm: Inorder(Tree)
1. Repeat step 2 – 4 while Tree != Null
2. Inorder(Tree->left)
3. Write(Tree->Data)
4. Inorder(Tree->right)
5. End
Result : G, D, H, L, B, E, A, C, I,
In-order Traversal
Algorithm: Inorder(Tree)
1. Repeat step 2 – 4 while Tree != Null
2. Inorder(Tree->left)
3. Write(Tree->Data)
4. Inorder(Tree->right)
5. End
Result : G, D, H, L, B, E, A, C, I,
F,
In-order Traversal
Algorithm: Inorder(Tree)
1. Repeat step 2 – 4 while Tree != Null
2. Inorder(Tree->left)
3. Write(Tree->Data)
4. Inorder(Tree->right)
5. End
Result : G, D, H, L, B, E, A, C, I,
F,
In-order Traversal
Algorithm: Inorder(Tree)
1. Repeat step 2 – 4 while Tree != Null
2. Inorder(Tree->left)
3. Write(Tree->Data)
4. Inorder(Tree->right)
5. End
Result : G, D, H, L, B, E, A, C, I,
F, K,
In-order Traversal
Algorithm: Inorder(Tree)
1. Repeat step 2 – 4 while Tree != Null
2. Inorder(Tree->left)
3. Write(Tree->Data)
4. Inorder(Tree->right)
5. End
Result : G, D, H, L, B, E, A, C, I,
F, K, J
In-order Traversal
Algorithm: Inorder(Tree)
1. Repeat step 2 – 4 while Tree != Null
2. Inorder(Tree->left)
3. Write(Tree->Data)
4. Inorder(Tree->right)
5. End
Result : G, D, H, L, B, E, A, C, I,
F, K, J
struct node
In-order Traversal root->right = insertNode(root->right,val);
{
int key; if(root->key > val)
struct node *left; root->left = insertNode(root->left,val);
struct node *right; return root;
}; }
struct node *insertNode(struct node *root, int val) void inorder(struct node *root)
{ {
if(root == NULL) if(root == NULL)
{ return;
struct node *newNode; //traverse the left subtree
inorder(root->left);
newNode = malloc(sizeof(struct node));
newNode->key = val; //visit the root
newNode->left = NULL; printf("%d ",root->key);
newNode->right = NULL;
return newNode; //traverse the right subtree
} inorder(root->right);
if(root->key < val) }
Pre – order Traversal
• Visit root node, Traverse left node , Traverse right node

Visit root node A


Traverse Left node– B
Visit root node B
No left child for B subtree
No right child for B subtree
A–B–C Traverse Right node– C
Visit root node C
No left child for C subtree
No right child for C subtree
Pre-order Traversal
Algorithm: Preorder(Tree)
1. Repeat step 2 – 4 while Tree != Null
2. Write(Tree->Data)
3. Preorder(Tree->left)
4. Preorder(Tree->right)
5. End
Pre-order Traversal
Algorithm: Preorder(Tree)
1. Repeat step 2 – 4 while Tree != Null
2. Write(Tree->Data)
3. Preorder(Tree->left)
4. Preorder(Tree->right)
5. End

Result: A,
Pre-order Traversal
Algorithm: Preorder(Tree)
1. Repeat step 2 – 4 while Tree != Null
2. Write(Tree->Data)
3. Preorder(Tree->left)
4. Preorder(Tree->right)
5. End

Result: A,
Pre-order Traversal
Algorithm: Preorder(Tree)
1. Repeat step 2 – 4 while Tree != Null
2. Write(Tree->Data)
3. Preorder(Tree->left)
4. Preorder(Tree->right)
5. End

Result: A, B,
Pre-order Traversal
Algorithm: Preorder(Tree)
1. Repeat step 2 – 4 while Tree != Null
2. Write(Tree->Data)
3. Preorder(Tree->left)
4. Preorder(Tree->right)
5. End

Result: A, B,
Pre-order Traversal
Algorithm: Preorder(Tree)
1. Repeat step 2 – 4 while Tree != Null
2. Write(Tree->Data)
3. Preorder(Tree->left)
4. Preorder(Tree->right)
5. End

Result: A, B, D,
Pre-order Traversal
Algorithm: Preorder(Tree)
1. Repeat step 2 – 4 while Tree != Null
2. Write(Tree->Data)
3. Preorder(Tree->left)
4. Preorder(Tree->right)
5. End

Result: A, B, D,G,
Pre-order Traversal
Algorithm: Preorder(Tree)
1. Repeat step 2 – 4 while Tree != Null
2. Write(Tree->Data)
3. Preorder(Tree->left)
4. Preorder(Tree->right)
5. End

Result: A, B, D, G,
Pre-order Traversal
Algorithm: Preorder(Tree)
1. Repeat step 2 – 4 while Tree != Null
2. Write(Tree->Data)
3. Preorder(Tree->left)
4. Preorder(Tree->right)
5. End

Result: A, B, D, G, H,
Pre-order Traversal
Algorithm: Preorder(Tree)
1. Repeat step 2 – 4 while Tree != Null
2. Write(Tree->Data)
3. Preorder(Tree->left)
4. Preorder(Tree->right)
5. End

Result: A, B, D, G, H, L,
Pre-order Traversal
Algorithm: Preorder(Tree)
1. Repeat step 2 – 4 while Tree != Null
2. Write(Tree->Data)
3. Preorder(Tree->left)
4. Preorder(Tree->right)
5. End

Result: A, B, D, G, H, L, E,
Pre-order Traversal
Algorithm: Preorder(Tree)
1. Repeat step 2 – 4 while Tree != Null
2. Write(Tree->Data)
3. Preorder(Tree->left)
4. Preorder(Tree->right)
5. End

Result: A, B, D, G, H, L, E,
Pre-order Traversal
Algorithm: Preorder(Tree)
1. Repeat step 2 – 4 while Tree != Null
2. Write(Tree->Data)
3. Preorder(Tree->left)
4. Preorder(Tree->right)
5. End

Result: A, B, D, G, H, L, E, C,
Pre-order Traversal
Algorithm: Preorder(Tree)
1. Repeat step 2 – 4 while Tree != Null
2. Write(Tree->Data)
3. Preorder(Tree->left)
4. Preorder(Tree->right)
5. End

Result: A, B, D, G, H, L, E, C,
Pre-order Traversal
Algorithm: Preorder(Tree)
1. Repeat step 2 – 4 while Tree != Null
2. Write(Tree->Data)
3. Preorder(Tree->left)
4. Preorder(Tree->right)
5. End

Result: A, B, D, G, H, L, E, C, F,
Pre-order Traversal
Algorithm: Preorder(Tree)
1. Repeat step 2 – 4 while Tree != Null
2. Write(Tree->Data)
3. Preorder(Tree->left)
4. Preorder(Tree->right)
5. End

Result: A, B, D, G, H, L, E, C, F,
I,
Pre-order Traversal
Algorithm: Preorder(Tree)
1. Repeat step 2 – 4 while Tree != Null
2. Write(Tree->Data)
3. Preorder(Tree->left)
4. Preorder(Tree->right)
5. End

Result: A, B, D, G, H, L, E, C, F,
I, J,
Pre-order Traversal
Algorithm: Preorder(Tree)
1. Repeat step 2 – 4 while Tree != Null
2. Write(Tree->Data)
3. Preorder(Tree->left)
4. Preorder(Tree->right)
5. End

Result: A, B, D, G, H, L, E, C, F,
I, J, K
Pre-order Traversal
Algorithm: Preorder(Tree)
1. Repeat step 2 – 4 while Tree != Null
2. Write(Tree->Data)
3. Preorder(Tree->left)
4. Preorder(Tree->right)
5. End

Result: A, B, D, G, H, L, E, C, F,
I, J, K
Post– order Traversal
• Traverse left node, Traverse right node , visit root node

Traverse Left node – B


No left child for B subtree
No right child for B subtree
Visit root node B
Traverse Right node– C
B–C–A No left child for C subtree
No right child for C subtree
Visit root node C
Visit root node A
Post-order Traversal
Algorithm: Postorder(Tree)
1. Repeat step 2 – 4 while Tree != Null
2. Postorder(Tree->left)
3. Postorder(Tree->right)
4. Write(Tree->Data)
5. End
Post-order Traversal
Algorithm: Postorder(Tree)
• Step 1 - Traverse the left subtree recursively.
• Step 2 - Traverse the right subtree recursively.
• Step 3 - Visit the root node.
Post-order Traversal
Algorithm: Postorder(Tree)
1. Repeat step 2 – 4 while Tree != Null
2. Postorder(Tree->left)
3. Postorder(Tree->right)
4. Write(Tree->Data)
5. End
Post-order Traversal
Algorithm: Postorder(Tree)
1. Repeat step 2 – 4 while Tree != Null
2. Postorder(Tree->left)
3. Postorder(Tree->right)
4. Write(Tree->Data)
5. End
Post-order Traversal
Algorithm: Postorder(Tree)
1. Repeat step 2 – 4 while Tree != Null
2. Postorder(Tree->left)
3. Postorder(Tree->right)
4. Write(Tree->Data)
5. End
Post-order Traversal
Algorithm: Postorder(Tree)
1. Repeat step 2 – 4 while Tree != Null
2. Postorder(Tree->left)
3. Postorder(Tree->right)
4. Write(Tree->Data)
5. End
Result : G,
Post-order Traversal
Algorithm: Postorder(Tree)
1. Repeat step 2 – 4 while Tree != Null
2. Postorder(Tree->left)
3. Postorder(Tree->right)
4. Write(Tree->Data)
5. End
Result : G,
Post-order Traversal
Algorithm: Postorder(Tree)
1. Repeat step 2 – 4 while Tree != Null
2. Postorder(Tree->left)
3. Postorder(Tree->right)
4. Write(Tree->Data)
5. End
Result : G, L,
Post-order Traversal
Algorithm: Postorder(Tree)
1. Repeat step 2 – 4 while Tree != Null
2. Postorder(Tree->left)
3. Postorder(Tree->right)
4. Write(Tree->Data)
5. End
Result : G, L, H,
Post-order Traversal
Algorithm: Postorder(Tree)
1. Repeat step 2 – 4 while Tree != Null
2. Postorder(Tree->left)
3. Postorder(Tree->right)
4. Write(Tree->Data)
5. End
Result : G, L, H, D
Post-order Traversal
Algorithm: Postorder(Tree)
1. Repeat step 2 – 4 while Tree != Null
2. Postorder(Tree->left)
3. Postorder(Tree->right)
4. Write(Tree->Data)
5. End
Result : G, L, H, D, E,
Post-order Traversal
Algorithm: Postorder(Tree)
1. Repeat step 2 – 4 while Tree != Null
2. Postorder(Tree->left)
3. Postorder(Tree->right)
4. Write(Tree->Data)
5. End
Result : G, L, H, D, E, B,
Post-order Traversal
Algorithm: Postorder(Tree)
1. Repeat step 2 – 4 while Tree != Null
2. Postorder(Tree->left)
3. Postorder(Tree->right)
4. Write(Tree->Data)
5. End
Result : G, L, H, D, E, B,
Post-order Traversal
Algorithm: Postorder(Tree)
1. Repeat step 2 – 4 while Tree != Null
2. Postorder(Tree->left)
3. Postorder(Tree->right)
4. Write(Tree->Data)
5. End
Result : G, L, H, D, E, B,
Post-order Traversal
Algorithm: Postorder(Tree)
1. Repeat step 2 – 4 while Tree != Null
2. Postorder(Tree->left)
3. Postorder(Tree->right)
4. Write(Tree->Data)
5. End
Result : G, L, H, D, E, B, I,
Post-order Traversal
Algorithm: Postorder(Tree)
1. Repeat step 2 – 4 while Tree != Null
2. Postorder(Tree->left)
3. Postorder(Tree->right)
4. Write(Tree->Data)
5. End
Result : G, L, H, D, E, B, I,
Post-order Traversal
Algorithm: Postorder(Tree)
1. Repeat step 2 – 4 while Tree != Null
2. Postorder(Tree->left)
3. Postorder(Tree->right)
4. Write(Tree->Data)
5. End
Result : G, L, H, D, E, B, I, K,
Post-order Traversal
Algorithm: Postorder(Tree)
1. Repeat step 2 – 4 while Tree != Null
2. Postorder(Tree->left)
3. Postorder(Tree->right)
4. Write(Tree->Data)
5. End
Result : G, L, H, D, E, B, I, K, J,
Post-order Traversal
Algorithm: Postorder(Tree)
1. Repeat step 2 – 4 while Tree != Null
2. Postorder(Tree->left)
3. Postorder(Tree->right)
4. Write(Tree->Data)
5. End
Result : G, L, H, D, E, B, I, K, J,
F,
Post-order Traversal
Algorithm: Postorder(Tree)
1. Repeat step 2 – 4 while Tree != Null
2. Postorder(Tree->left)
3. Postorder(Tree->right)
4. Write(Tree->Data)
5. End
Result : G, L, H, D, E, B, I, K, J,
F, C,
Post-order Traversal
Algorithm: Postorder(Tree)
1. Repeat step 2 – 4 while Tree != Null
2. Postorder(Tree->left)
3. Postorder(Tree->right)
4. Write(Tree->Data)
5. End
Result : G, L, H, D, E, B, I, K, J,
F, C, A
Post-order Traversal
Algorithm: Postorder(Tree)
1. Repeat step 2 – 4 while Tree != Null
2. Postorder(Tree->left)
3. Postorder(Tree->right)
4. Write(Tree->Data)
5. End
Result : G, L, H, D, E, B, I, K, J,
F, C, A
Tree
struct Node{
Traversal printf("1. Insert\n2. Display\n3. Exit");
int data; printf("\nEnter your choice: ");
struct Node *left; scanf("%d",&choice);
struct Node *right; switch(choice)
}; {
struct Node *root = NULL; case 1: printf("\nEnter the value to be insert: ");
int count = 0; scanf("%d", &value);
struct Node* insert(struct Node*, int); root = insert(root,value); break;
void display(struct Node*); case 2: display(root); break;
case 3: exit(0);
void main(){ default: printf("\nPlease select correct operations!!!\n");
int choice, value; }
clrscr(); }
printf("\n----- Binary Tree -----\n"); }
while(1){
Tree Traversal
struct Node* insert(struct Node *root,int value)
{ else
struct Node *newNode; root->right = insert(root->right,value);
newNode = (struct Node*)malloc(sizeof(struct Node)); }
newNode->data = value; return root;
if(root == NULL) }
{ void display(struct Node *root)
newNode->left = newNode->right = NULL; {
root = newNode; if(root != NULL)
count++; {
} display(root->left);
else printf("%d\t",root->data);
{ display(root->right);
if(count%2 != 0) }
root->left = insert(root->left,value); }
21CSC201J
DATA STRUCTURES AND
ALGORITHMS

UNIT-4
Topic : BINARY SEARCH TREE
BINARY SEARCH TREE
• Binary Search Tree is a binary tree in which every node contains only smaller
values in its left sub tree and only larger values in its right sub tree.
• Also called as ORDERED binary tree
BST– properties:
• It should be Binary tree.
• Left subtree < Root Node < = Right subtree
(or)
Left subtree < =Root Node < Right subtree
BINARY SEARCH TREE
Binary search trees or not ?
• Operations: Searching, Insertion, Deletion of a Node
• TimeComplexity :
BEST CASE WORST CASE

Search Operation - O(log n) Search Operation - O(n)


Insertion Operation- O(log n) Insertion Operation- O(1)
Deletion Operation - O(log n) Deletion Operation - O(n)
(note:Height of the binary search tree becomes n)
Example: Example:

(a) Left skewed, and (b) right skewed binary search


trees
Binary search tree Construction
• Create a binary search tree using the following data elements: 45, 39,
56, 12, 34, 78, 32, 10, 89, 54, 67, 81
Remaining: 89, 54, 67, 81
SEARCHING A NODE IN BST
SearchElement (TREE, VAL)

Step 1: IF TREE-> DATA = VAL OR TREE = NULL


Return TREE
ELSE
IF VAL < TREE-> DATA
Return searchElement(TREE-> LEFT, VAL)
ELSE
Return searchElement(TREE-> RIGHT, VAL)
[END OF IF]
[END OF IF]
Step 2: END
EXAMPLE 1:
Searching a node with value 12 in the given binary search tree

We start our search from the root node 45.


As 12 < 45, so we search in 45’s LEFT subtree.
As 12 < 39, so we search in 39’s LEFT subtree.
So, we conclude that 12 is present in the above BST.
EXAMPLE 2:
Searching a node with value 52 in the given binary search tree

We start our search from the root node 45.


As 52 > 45, so we search in 45’s RIGHT subtree.
As 52 < 56 so we search in 56’s LEFT subtree.
As 52 < 54 so we search in 54’s LEFT subtree.
But 54 is leaf node
So, we conclude that 52 is not present in the above BST.
INSERTING A NODE IN BST
Insert (TREE, VAL)
Step 1: IF TREE = NULL
Allocate memory for TREE
SET TREE-> DATA = VAL
SET TREE-> LEFT = TREE-> RIGHT = NULL
ELSE
IF VAL < TREE-> DATA
Insert(TREE-> LEFT, VAL)
ELSE
Insert(TREE-> RIGHT, VAL)
[END OF IF]
[END OF IF]

Step 2: END
EXAMPLE : Inserting nodes with values 55 in the given binary search tree

We start searching for value 55 from the root node 45.


As 55 > 45, so we search in 45’s RIGHT subtree.
As 55 < 56 so we search in 56’s LEFT subtree.
As 55 > 54 so so we add 55 to 54’s right subtree.
Deletion Operation in BST
• Case 1: Deleting a Leaf node (A node with no children)
• Case 2: Deleting a node with one child
• Case 3: Deleting a node with two children
Case 1: Deleting a Leaf node (A node with no children)
EXAMPLE : Deleting node 78 from the given binary search tree
Case 2: Deleting a node with one child
EXAMPLE : Deleting node 54 from the given binary search tree
Case 3: Deleting a node with two children
EXAMPLE 1 : Deleting node 56 from the given binary search tree

 Visit to the left sub tree of the deleting node.


 Grab the greatest value element called as in-order predecessor.
 Replace the deleting element with its in-order predecessor.
Case 3: Deleting a node with two children
EXAMPLE 2 : Deleting node 15 from the given binary search tree

 Visit to the right sub tree of the deleting node.


 Pluck the least value element called as inorder successor.
 Replace the deleting element with its inorder successor.
Deletion Operation in BST
Delete (TREE, VAL)
Step 1: IF TREE = NULL SET TEMP = TREE
Write "VAL not found in the tree" IF TREE-> LEFT = NULL AND TREE-> RIGHT = NULL
ELSE IF VAL < TREE-> DATA SET TREE = NULL
Delete(TREE->LEFT, VAL) ELSE IF TREE-> LEFT != NULL
ELSE IF VAL > TREE-> DATA SET TREE = TREE-> LEFT
Delete(TREE-> RIGHT, VAL) ELSE
ELSE IF TREE-> LEFT AND TREE-> RIGHT SET TREE = TREE-> RIGHT
SET TEMP = findLargestNode(TREE-> LEFT) ( INORDER PREDECESSOR) [END OF IF]
SET TREE-> DATA = TEMP DATA FREE TEMP
Delete(TREE-> LEFT, TEMP DATA) [END OF IF] Step 2: END
(OR)
SET TEMP = findSmallestNode(TREE-> RIGHT) ( INORDER SUCESSOR)
SET TREE-> DATA = TEMP DATA
Delete(TREE-> RIGHT, TEMP DATA)
ELSE
21CSC201J
DATA STRUCTURES AND
ALGORITHMS

UNIT-4
Topic : AVL TREE
AVL TREE
• An AVL tree is a balanced binary search tree.
• In AVL Tree balance factor of every node is either -1, 0 or +1.

• Balance factor = height Of Left Subtree – height Of Right Subtree


AVL TREE
• Named after Adelson-Velskii and Landis as AVL tree
• Also called as self-balancing binary search tree
AVL tree – properties:
• It should be Binary search tree
• Balancing factor: balance of every node is either -1 or 0 or 1
where balance(node) = height(node.left subtree) – height(node.right subtree)
• Maximum possible number of nodes in AVL tree of height H
= 2H+1 – 1
• Operations: Searching, Insertion, Deletion of a Node
• TimeComplexity : O(log n)
Balancing Factor
Balance factor = heightOfLeftSubtree – heightOfRightSubtree
Example 1 : Check - AVL Tree?
Example 2: Check - AVL Tree?
6

4 8

1 5 7 11

2
operations on AVL tree

1.SEARCHING
2.INSERTION
3.DELETION
Search Operation in AVL Tree
ALGORITHM:
STEPS:
1 : Get the search element
2 : check search element == root node in the tree.
3 : If both are exact match, then display “element found" and end.
4 : If both are not matched, then check whether search element is < or > than that node
value.
5: If search element is < then continue search in left sub tree.
6: If search element is > then continue search in right sub tree.
7: Repeat the step from 1 to 6 until we find the exact element
8: Still the search element is not found after reaching the leaf node display “element not
found”.
INSERTION or DELETION
• After performing any operation on AVL tree - the balance factor of each node is to be checked.
• After insertion or deletion there exists either any one of the following:

Scenario 1:
• After insertion or deletion , the balance factor of each node is either 0 or 1 or -1.
• If so AVL tree is considered to be balanced.
• The operation ends.
Scenario 1:
• After insertion or deletion, the balance factor is not 0 or 1 or -1 for at least one node then
• The AVL tree is considered to be imbalanced.
• If so, Rotations are need to be performed to balance the tree in order to make it as AVL TREE.
Rotation
• After insertion / deletion balance factor of every node in the tree 0,1-1.
• Otherwise we must make it balanced.
• Whenever the tree becomes imbalanced due to any operation we
do rotation operations to make the tree balanced.

• Rotation is the process of moving nodes either to left or to right to make the tree
balanced.
AVL TREE ROTATION
LL ROTATION
new node is inserted in the left sub-tree of the left sub-tree of the node (
LL imbalanced)
LL ROTATION
When new node is inserted in the left sub-tree of the left sub-tree of the
critical
LL ROTATION- Example
RR ROTATION
new node is inserted in the right sub-tree of the right sub-tree of the node
(RR Imbalanced)
RR ROTATION
When new node is inserted in the right sub-tree of the right sub-tree of
the critical
RR Rotation - Example
LR ROTATION
When new node is inserted in the left sub-tree of the right sub-
tree of the node LR Imbalance
LR ROTATION
When new node is inserted in the left sub-tree of the right sub-
tree of the node
LR ROTATION
When new node is inserted in the left sub-tree of the right sub-
tree of the critical
LR Rotation - Example
RL ROTATION
When new node is inserted in the right sub-tree of the left sub-
tree of the node ( RL imbalance)
RL ROTATION
When new node is inserted in the right sub-tree of the left sub-
tree of the node ( RL imbalance)
RL ROTATION
When new node is inserted in the right sub-tree of the left sub-
tree of the node ( RL imbalance)
RL ROTATION
When new node is inserted in the right sub-tree of the left sub-
tree of the critical
RL Rotation - Example
AVL TREE CONSTRUCTION / NODE INSERTION - Example
Construct an AVL tree by inserting the following elements in the given order 63, 9, 19, 27, 18, 108,
99, 81.
AVL TREE CONSTRUCTION / NODE INSERTION - Example
In an organization, 10 employees joined with their ID 50,20, 60, 10, 8, 15, 32, 46, 11, 48. Kindly insert
them one by one but follow the Height balance while constructing the tree. Write the Steps to follow the
insertion and solve it stepwise
AVL TREE – NODE DELETION - Example
• Delete nodes 52, 36, and 61 from the AVL tree given
Construct AVL Tree
• 35, 15,5,20,25,17,45
21CSC201J
DATA STRUCTURES AND
ALGORITHMS

UNIT-4
Topic : B TREE
B-Trees
• A B-tree is called as an m-way tree. Its order is m.

• Each node can have a maximum of m children.


• Non-leaf nodes (internal node) minimum m / 2 children.
• Root can have node minimum 2 children & leaf – min – 0 child.

• Each non-leaf node can have maximum m-1 keys


• Minimum no of key at root is 1 and other node is m / 2 -1

• All the leaf nodes should be in the same level


Structure of an m-way
search tree node
• The structure of an m-way search tree node is shown in figure

• Where P0 , P1 , P2 , ..., Pn are pointers to the node’s sub-trees and K0


, K1 , K2 , ..., Kn–1 are the key values of the node.
• All the key values are stored in ascending order.
• A B tree is a specialized m-way tree developed by Rudolf Bayer and Ed
McCreight in 1970 that is widely used for disk access.
Source :
http://masterraghu.com/subjects/Datastructures/ebooks/rema
%20thareja.pdf
B-Trees - Examples

B-Tree of order 3
B-Trees - Examples

B-Tree of order 4
Searching in a B-Tree
• Similar to searching in a binary search tree.
• Consider the B-Tree shown here.
• If we wish to search for 72 in this tree, first consider
the root, the search key is greater than the values in
the root node. So go to the right sub-tree.
• The right sub tree consists of two values and again 72 is greater than 63 so traverse to right sub tree of
63.
• The right sub tree consists of two values 72 and 81. So we found our value 72.
Insertion in a B-Tree
• Insertions are performed at the leaf level.
• Search the B-Tree to find suitable place to insert the new element.
• If the leaf node is not full, the new element can be inserted in the leaf
level.
• If the leaf is full,
• insert the new element in order into the existing set of keys.
• split the node at its median into two nodes.
• push the median element up to its parent’s node. If the parent’s node is
already full, then split the parent node by following the same steps.
Insertion - Example
• Consider the B-Tree of order 5

• Insert 8, 9, 39 and 4 into it.


Insertion - Example
• Insert 8, 9, 39 and 4 into it.
Insertion - Example
Exercise
• Consider the B-Tree of order 3, try to insert 121 and 87.

• Create a B-Tree of order 5, by inserting the following elements


• 3, 14, 7, 1, 8, 5, 11, 17, 13, 6, 23, 12, 20, 26, 4, 16, 18, 24, 25, and 19.
Deletion in a B-Tree
• Like insertion, deletion also should be performed from leaf nodes.
• There are two cases in deletion.
• To delete a leaf node.
• To delete an internal node.
• Deleting leaf node
• Search for the element to be deleted, if it is in the leaf node and the leaf node
has more than m/2 elements then delete the element.
• If the leaf node does not contain m/2 elements then take an element from
either left or right sub tree.
• If both the left and right sub tree contain only minimum number of elements
then create a new leaf node.
Deletion in a B-Tree
• Deleting an internal node
• If the element to be deleted is in an internal node then find the predecessor
or successor of the element to be deleted and place it in the deleted element
position.
• The predecessor or successor of the element to be deleted will always be in
the leaf node.
• So the procedure will be similar to deleting an element from the leaf node.
Deletion - Example
• Consider the B-Tree of order 5

Try to delete values 93, 201, 180 and 72 from it.


Deletion - Example
• Try to delete values 93, 201, 180 and 72 from it.
Deletion - Example
Deletion - Example
Deletion - Example
Exercise
• Consider the B-Tree of order 3, try to delete 36 and 109
21CSC201J
DATA STRUCTURES AND
ALGORITHMS

UNIT-4
Topic : Heap
Heaps
• A heap is a complete binary tree in which each node can have utmost
two children.
• Heap is a balanced binary tree where the root-node key is compared
with its children and arranged accordingly.

• Min-Heap − the value of the root node is less than or equal to either
of its children
• Max-Heap − the value of the root node is greater than or equal to
either of its children.
Heaps
• A heap is a binary tree whose left and right subtrees have values less
than their parents.
• The root of a maxheap is guaranteed to hold the largest node in the
tree; its subtrees contain data that have lesser values.
• Unlike the binary search tree, however, the lesser-valued nodes of a
heap can be placed on either the right or the left subtree.
• Heaps have another interesting facet: they are often implemented in
an array rather than a linked list.
Heaps
• Max-Heap − the value of the root node is greater than or equal to
either of its children.
Heaps : Max-Heap Construction/Insertion
• Step 1 − Create a new node at the end of heap.
• Step 2 − Assign new value to the node.
• Step 3 − Compare the value of this child node with its parent.
• Step 4 − If value of parent is less than child, then swap them.
• Step 5 − Repeat step 3 & 4 until Heap property holds.
Heaps : Max-Heap Construction/Insertion
• Construct heap data structure: 35, 33, 42, 10, 14, 19, 27, 44, 26, 31
Heaps : Max-Heap Deletion
• Step 1 − Remove root node.
• Step 2 − Move the last element of last level to root.
• Step 3 − Compare the value of this child node with its parent.
• Step 4 − If value of parent is less than child, then swap them.
• Step 5 − Repeat step 3 & 4 until Heap property holds.
Heaps : Max-Heap Deletion
• In Deletion in the heap tree, the root node is always deleted and it is
replaced with the last element.
Definition

• A heap, as shown in Figure


9-1, is a binary tree
structure with the
following
• properties:
• 1. The tree is complete or
nearly complete.
• 2. The key value of each
node is greater than or A heap is a complete or nearly complete binary
tree in which the key value in a node is greater
equal to the key value in than or
each of its descendants. equal to the key values in all of its subtrees, and
the subtrees are in turn heaps.
Heap Operations
• Two basic maintenance operations are performed on a heap:
• insert a node
• and delete a node.

• To implement the insert and delete operations, we need two basic algorithms:
reheap up and reheap down.
Reheap Up
• The reheap up operation reorders a “broken” heap by floating the
last element up the tree until it is in its correct location in the heap.
Heap Implementation
• Although a heap can be built in a dynamic tree structure, it is most often
implemented in an array. This implementation is possible because the heap is, by
definition, complete or nearly complete. Therefore, the relationship
• between a node and its children is fixed and can be calculated as shown below.
• 1. For a node located at index i, its children are found at:
• a. Left child: 2i + 1
• b. Right child: 2i + 2
• 2. The parent of a node located at index i is located at [(i – 1) / 2]
• 3. Given the index for a left child, j, its right sibling, if any, is found at j + 1.
• Conversely, given the index for a right child, k, its left sibling, which must exist, is
found at k – 1
• 4. Given the size, n, of a complete heap, the location of the first
leaf is [(n / 2)]
• Given the location of the first leaf element, the location of the
last non leaf
• element is one less.

parent= (i-1)/2
parent of 40, i=4
parent=3/2=1= 75
A heap can be implemented in an
array because it must be a complete
or nearly complete binary tree, which
allows a fixed relationship between
each node and its children.
Max Heap Construction Algorithm
Example :max heap. Insert a new node with
value 85.
• Step 1 - Insert the newNode with value 85
as last leaf from left to right. That means • Step 2 - Compare newNode value (85) with
newNode is added as a right child of node with
value 75. After adding max heap is as follows... its Parent node value (75). That means 85 > 75
• Step3 - Here newNode value (85) is • Step 4 - Now, again compare new Node
greater than its parent value (75), value (85) with its parent node value (89).
then swap both of them. After
swapping, max heap is as follows...
• Here, newNode value (85) is smaller than its parent node value
(89). So, we stop insertion process. Finally, max heap after
insertion of a new node with value 85 is as follows...
Max Heap Deletion Algorithm
Problem Example:

Step 0: Change the Array Data Structure into a Tree


In order to change an array structure into the tree version of the binary heap, we start from the
left to the right of the array, and then insert values into the binary heap from top to bottom
and left to right.
• Step 1: Delete the node that contains the
value you want deleted in the heap. Step 2: Replace the deleted node with the
farthest right node.
The value that we want to delete is the
maximum value or element in the array
which is at the root of the tree. This is
the node that contains the value “10”.
Step 3: Heapify (Fix the heap):

• The value “7” at the root of the tree is less than both of its children,
the nodes containing the value “8” and “9”. We need to swap the “7”
with the largest child, the node containing the value “9”.
Heap sort
• Heaps can be used in sorting an array.
• In max-heaps, maximum element will always be at the root. Heap Sort
uses this property of heap to sort the array.
• Consider an array Arr which is to be sorted using Heap Sort.
• 1. Initially build a max heap of elements in Arr.
• 2. The root element, that is Arr[1], will contain maximum element of Arr.
After that, swap this element with the last element of Arr and heapify
the max heap excluding the last element which is already in its correct
position and then decrease the length of heap by one.
• 3. Repeat the step 2, until all the elements are in their correct position.
Heap sort-complexity

• max_heapify has complexity O(logN),


• build_maxheap has complexity O(N) and we run
max_heapify N−1 times in heap_sort function, therefore complexity
of heap_sort function is O(NlogN).
Heap Applications
Three common applications of heaps
• Three common applications of heaps are
• selection algorithms,
• Priority queues, and
• sorting.
Selection Algorithms
• There are two solutions to the problem of determining the kth element in
an
• unsorted list.
• We could first sort the list and select the element at location k, or we could
create a heap and delete k – 1 elements from it, leaving the desired
element at the root.
• Rather than simply discard the elements at the top of the heap, a better
solution is to place the deleted element at the end of the heap and reduce
the heap size by 1.
• After the kth element has been processed, the temporarily removed
elements can then be reinserted into the heap.
For example, if we want to know the fourth-largest
element in a list, we can create the heap shown in Figure
9-14. After deleting three times, we have the fourth-largest
element, 21, at the top of the heap.
After selecting 21 we re-heap to restore the heap so that it
is complete and we are ready for another selection.
Priority Queues
• The heap is an excellent structure to use for a priority queue.
• As an event enters the queue, it is assigned a priority number that
determines its position relative to the other events already in the
queue.
• It is assigned a priority number even though the new event can enter
the heap in only one place at any given time, the first empty leaf.
Hashing and Collision Resolution

Hashing
Hashing is the process of mapping large amount of data item to smaller table with
the help of hashing function. Hashing is also known as Hashing Algorithm or Message Digest
Function.

It is a technique to convert a range of key values into a range of indexes of an array.


Hashing is a well-known technique to search any particular element among several elements. It
minimizes the number of comparisons while performing the search.

Hashing Mechanism
In hashing, an array data structure called as Hash table is used to store the data
items.
Based on the hash key value, data items are inserted into the hash table.
Hash Key Value (index)
Hash key value is a special value that serves as an index for a data item. It
indicates where the data item should be stored in the hash table. Hash key value is generated
using a hash function.
Hash Table
Hash table or hash map is a data structure used to store key-value pairs. It is a collection
of items stored to make it easy to find them later. It uses a hash function to compute an index
into an array of buckets or slots from which the desired value can be found.

Figure 1: Hash Table


The above figure shows the hash table with the size of n = 10. Each position of the hash table is
called as Slot. In the above hash table, there are n slots in the table, names = {0, 1, 2, 3, 4, 5, 6, 7,
8, 9}. Slot 0, slot 1, slot 2 and so on. Hash table contains no items, so every slot is empty.
As we know the mapping between an item and the slot where item belongs in the hash table is
called the hash function. The hash function takes any item in the collection and returns an integer
in the range of slot names between 0 to n-1.
1
Figure 2: Hashing Mechanism
Hash Function
Hash function is a function that maps any big number or string to a small integer value.
Hash function takes the data item as an input and returns a small integer value as an output. The
small integer value is called as a hash value. Hash value of the data item is then used as an index
for storing it into the hash table.

Types of Hash Functions


There are various types of hash functions available such as-
 Mid Square Hash Function
The key K is multiplied by itself and the address is obtained by selecting an appropriate number
of digits from the middle of the square.
The number of digits selected depends on the size of the table.

-digit address is required, positions 5 to 7 could be chosen giving address 138.


 Division Hash Function
Suppose we have integer items {26, 70, 18, 31, 54, 93}. One common method of determining a
hash key is the division method of hashing and the formula is :
Hash Key = Key Value % Number of Slots in the Table
Division method or reminder method takes an item and divides it by the table size and returns the
remainder as its hash value.

2
Data Item Value % No. of Slots Hash Value

26 26 % 10 = 6 6

70 70 % 10 = 0 0

18 18 % 10 = 8 8

31 31 % 10 = 1 1

54 54 % 10 = 4 4

93 93 % 10 = 3 3

Figure 3: Hash Table


After computing the hash values, we can insert each item into the hash table at the designated
position as shown in the above figure. In the hash table, 6 of the 10 slots are occupied, it is
referred to as the load factor
 Folding Hash Function
The key K is partitioned into a number of parts, each of which has the same length as the
required address with the possible exception of the last part .
final carry, to form an address.
a three digit address.
P1=356, P2=942, P3=781 are added to yield 079.
Collision Handling
When two or more keys are given the same hash value, it is called a collision. To handle this
collision, we use collision resolution techniques.

3
Figure 4: Collision Handling
Keys: 5, 28, 19, 15, 20, 33, 12, 17, 10
HT slots: 9
hash function = h(k) = k % 9
h(5) = 5 % 9 = 5
h(28) = 28 % 9 = 1
h(19) = 19 % 9 = 1
h(15) = 15 % 9 = 6
h(20) = 20 % 9 = 2
h(33) = 33 % 9 = 6
h(12) = 12 % 9 = 3
h(17) = 17 % 9 = 8
h(10) = 10 % 9 = 1
Collision resolution techniques
There are two types of collision resolution techniques.
1. Separate chaining (open hashing)
2. Open addressing (closed hashing)

4
Figure 5: Techniques in Collision Resolution
Separate
chaining
In this technique, a linked list is created from the slot in which collision has occurred, after
which the new key is inserted into the linked list. This linked list of slots looks like a chain, so it
is called separate chaining. It is used more when we do not know how many keys to insert or
delete.
Problem-
Using the hash function ‘key mod 7’, insert the following sequence of keys in the hash table-
50, 700, 76, 85, 92, 73 and 101
Use separate chaining technique for collision resolution.
Solution-
The given sequence of keys will be inserted in the hash table as-
Step-01:
Draw an empty hash table.
For the given hash function, the possible range of hash values is [0, 6].
So, draw an empty hash table consisting of 7 buckets as-

5
Figure 6: Empty Hash Table
Step-02:
Insert the given keys in the hash table one by one.
The first key to be inserted in the hash table = 50.
Bucket of the hash table to which key 50 maps = 50 mod 7 = 1.
So, key 50 will be inserted in bucket-1 of the hash table as-

Figure 7: Insert 50
Step-03:
The next key to be inserted in the hash table = 700.
Bucket of the hash table to which key 700 maps = 700 mod 7 = 0.
So, key 700 will be inserted in bucket-0 of the hash table as-

6
Figure 8: Insert 700
Step-04:
 The next key to be inserted in the hash table = 76.
 Bucket of the hash table to which key 76 maps = 76 mod 7 = 6.
 So, key 76 will be inserted in bucket-6 of the hash table as-

Figure 9: Insert 76

Step-05:
 The next key to be inserted in the hash table = 85.
 Bucket of the hash table to which key 85 maps = 85 mod 7 = 1.
 Since bucket-1 is already occupied, so collision occurs.
 Separate chaining handles the collision by creating a linked list to bucket-1.
 So, key 85 will be inserted in bucket-1 of the hash table as-

7
Figure 10: Insert 85

Step-06:
 The next key to be inserted in the hash table = 92.
 Bucket of the hash table to which key 92 maps = 92 mod 7 = 1.
 Since bucket-1 is already occupied, so collision occurs.
 Separate chaining handles the collision by creating a linked list to bucket-1.
 So, key 92 will be inserted in bucket-1 of the hash table as-

Figure 11: Insert 92

Step-07:
 The next key to be inserted in the hash table = 73.
 Bucket of the hash table to which key 73 maps = 73 mod 7 = 3.
 So, key 73 will be inserted in bucket-3 of the hash table as-

Figure 12: Insert 73

8
Step-08:
 The next key to be inserted in the hash table = 101.
 Bucket of the hash table to which key 101 maps = 101 mod 7 = 3.
 Since bucket-3 is already occupied, so collision occurs.
 Separate chaining handles the collision by creating a linked list to bucket-3.
 So, key 101 will be inserted in bucket-3 of the hash table as-

Figure 13: Insert 101


Open addressing
Open addressing is collision-resolution method that is used to control the collision in the hashing
table. There is no key stored outside of the hash table. Therefore, the size of the hash table is
always greater than or equal to the number of keys. It is also called closed hashing.
The following techniques are used in open addressing:
 Linear probing
 Quadratic probing
 Double hashing
1. Linear Probing-
In linear probing,
When collision occurs, we linearly probe for the next bucket.
We keep probing until an empty bucket is found.
Problem-
Using the hash function ‘key mod 7’, insert the following sequence of keys in the hash table-
50, 700, 76, 85, 92, 73 and 101
Use linear probing technique for collision resolution.
Solution-
The given sequence of keys will be inserted in the hash table as-

9
Step-01:
 Draw an empty hash table.
 For the given hash function, the possible range of hash values is [0, 6].
 So, draw an empty hash table consisting of 7 buckets as-

Figure 14: Empty Hash Table

Step-02:
 Insert the given keys in the hash table one by one.
 The first key to be inserted in the hash table = 50.
 Bucket of the hash table to which key 50 maps = 50 mod 7 = 1.
 So, key 50 will be inserted in bucket-1 of the hash table as-

Figure 15: Insert 50


Step-03:
 The next key to be inserted in the hash table = 700.
 Bucket of the hash table to which key 700 maps = 700 mod 7 = 0.
 So, key 700 will be inserted in bucket-0 of the hash table as-

10
Figure 16: Insert 700
Step-04:
 The next key to be inserted in the hash table = 76.
 Bucket of the hash table to which key 76 maps = 76 mod 7 = 6.
 So, key 76 will be inserted in bucket-6 of the hash table as-

Figure 17: Insert 76


Step-05:
 The next key to be inserted in the hash table = 85.
 Bucket of the hash table to which key 85 maps = 85 mod 7 = 1.
 Since bucket-1 is already occupied, so collision occurs.
 To handle the collision, linear probing technique keeps probing linearly until an empty
bucket is found.
 The first empty bucket is bucket-2.
 So, key 85 will be inserted in bucket-2 of the hash table as-

11
Figure 18: Insert 85
Step-06:
 The next key to be inserted in the hash table = 92.
 Bucket of the hash table to which key 92 maps = 92 mod 7 = 1.
 Since bucket-1 is already occupied, so collision occurs.
 To handle the collision, linear probing technique keeps probing linearly until an empty
bucket is found.
 The first empty bucket is bucket-3.
 So, key 92 will be inserted in bucket-3 of the hash table as-

Figure 19: Insert 92


Step-07:
 The next key to be inserted in the hash table = 73.
 Bucket of the hash table to which key 73 maps = 73 mod 7 = 3.
 Since bucket-3 is already occupied, so collision occurs.
 To handle the collision, linear probing technique keeps probing linearly until an empty
bucket is found.
 The first empty bucket is bucket-4.
 So, key 73 will be inserted in bucket-4 of the hash table as-

12
Figure 20: Insert 73
Step-08:
 The next key to be inserted in the hash table = 101.
 Bucket of the hash table to which key 101 maps = 101 mod 7 = 3.
 Since bucket-3 is already occupied, so collision occurs.
 To handle the collision, linear probing technique keeps probing linearly until an empty
bucket is found.
 The first empty bucket is bucket-5.
 So, key 101 will be inserted in bucket-5 of the hash table as-

Figure 21: Insert 101


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.

13
UNIT -4
COLLISION
the hash function is a function that returns the key value using which the record can be placed in
the hash table. Thus this function helps us in placing the record in the hash table at appropriate
position and due to this we can retrieve the record directly from that location. This function need
to be designed very carefully and it should not return the same hash key address for two different
records. This is an undesirable situation in hashing.

Definition: The situation in which the hash function returns the same hash key (home bucket) for
more than one record is called collision and two same hash keys returned for different records is
called synonym.

Similarly when there is no room for a new pair in the hash table then such a situation is
called overflow. Sometimes when we handle collision it may lead to overflow conditions.
Collision and overflow show the poor hash functions.

For example, 0
1 131
Consider a hash function. 2
3 43
H(key) = recordkey%10 having the hash table size of 10 4 44
5
The record keys to be placed are 6 36
7 57
131, 44, 43, 78, 19, 36, 57 and 77 8 78
131%10=1 9 19
44%10=4
43%10=3
78%10=8
19%10=9
36%10=6
57%10=7
77%10=7

Now if we try to place 77 in the hash table then we get the hash key to be 7 and at index 7 already
the record key 57 is placed. This situation is called collision. From the index 7 if we look for next
vacant position at subsequent indices 8.9 then we find that there is no room to place 77 in the hash
table. This situation is called overflow.

COLLISION RESOLUTION TECHNIQUES


If collision occurs then it should be handled by applying some techniques. Such a
technique is called collision handling technique.
1. Chaining
2. Open addressing (linear probing)
3.Quadratic probing
4. Double hashing
5. Double hashing
6.Rehashing

104
UNIT -4
CHAINING
In collision handling method chaining is a concept which introduces an additional field with data
i.e. chain. A separate chain table is maintained for colliding data. When collision occurs then a
linked list(chain) is maintained at the home bucket.

For eg;

Consider the keys to be placed in their home buckets are


131, 3, 4, 21, 61, 7, 97, 8, 9

then we will apply a hash function as H(key) = key % D

Where D is the size of table. The hash table will be-

Here D = 10

0
1 131 21 61 NULL

3 NULL

131 61 NULL

7 97 NULL

A chain is maintained for colliding elements. for instance 131 has a home bucket (key) 1.
similarly key 21 and 61 demand for home bucket 1. Hence a chain is maintained at index 1.

OPEN ADDRESSING – LINEAR PROBING


This is the easiest method of handling collision. When collision occurs i.e. when two records
demand for the same home bucket in the hash table then collision can be solved by placing the
second record linearly down whenever the empty bucket is found. When use linear probing (open
addressing), the hash table is represented as a one-dimensional array with indices that range from
0 to the desired table size-1. Before inserting any elements into this table, we must initialize the
table to represent the situation where all slots are empty. This allows us to detect overflows and
collisions when we inset elements into the table. Then using some suitable hash function the
element can be inserted into the hash table.

For example:

Consider that following keys are to be inserted in the hash table

131, 4, 8, 7, 21, 5, 31, 61, 9, 29

105
UNIT -4

Initially, we will put the following keys in the hash table.


We will use Division hash function. That means the keys are placed using the formula

H(key) = key % tablesize


H(key) = key % 10

For instance the element 131 can be placed at

H(key) = 131 % 10
=1

Index 1 will be the home bucket for 131. Continuing in this fashion we will place 4, 8, 7.

Now the next key to be inserted is 21. According to the hash function

H(key)=21%10
H(key) = 1

But the index 1 location is already occupied by 131 i.e. collision occurs. To resolve this collision
we will linearly move down and at the next empty location we will prob the element. Therefore
21 will be placed at the index 2. If the next element is 5 then we get the home bucket for 5 as
index 5 and this bucket is empty so we will put the element 5 at index 5.

Index Key Key Key

NULL NULL NULL


0
131 131 131
1
NULL 21 21
2
NULL NULL 31
3
4 4 4
4
NULL 5 5
5
NULL NULL 61
6
7 7 7
7
8 8 8
8
NULL NULL NULL
9

after placing keys 31, 61

106
UNIT -4
The next record key is 9. According to decision hash function it demands for the home bucket 9.
Hence we will place 9 at index 9. Now the next final record key 29 and it hashes a key 9. But
home bucket 9 is already occupied. And there is no next empty bucket as the table size is limited
to index 9. The overflow occurs. To handle it we move back to bucket 0 and is the location over
there is empty 29 will be placed at 0th index.
Problem with linear probing:
One major problem with linear probing is primary clustering. Primary clustering is a process in
which a block of data is formed in the hash table when collision is resolved.
Key

39
19%10 = 9 cluster is formed
18%10 = 8 29
39%10 = 9 8
29%10 = 9
8%10 = 8

rest of the table is empty

this cluster problem can be solved by quadratic probing.

18

QUADRATIC PROBING: 19

Quadratic probing operates by taking the original hash value and adding successive values of an
arbitrary quadratic polynomial to the starting value. This method uses following formula.

H(key) = (Hash(key) + i2) % m)

where m can be table size or any prime number.

for eg; If we have to insert following elements in the hash table with table size 10:

37, 90, 55, 22, 17, 49, 87 0 90


1 11
37 % 10 = 7 2 22
90 % 10 = 0 3
55 % 10 = 5 4
22 % 10 = 2 5 55
11 % 10 = 1 6
7 37
Now if we want to place 17 a collision will occur as 17%10 = 7 and 8
bucket 7 has already an element 37. Hence we will apply 9
quadratic probing to insert this record in the hash table.

Hi (key) = (Hash(key) + i2) % m

Consider i = 0 then
(17 + 02) % 10 = 7

107
UNIT -4
(17 + 12) % 10 = 8, when i =1

The bucket 8 is empty hence we will place the element at index 8. 0 90


Then comes 49 which will be placed at index 9. 1 11
2 22
49 % 10 = 9 3
4
5 55
6
7 37
8 49
9
Now to place 87 we will use quadratic probing.
0 90
(87 + 0) % 10 = 7 1 11
(87 + 1) % 10 = 8… but already occupied 2 22
(87 + 22) % 10 = 1.. already occupied 3
(87 + 32) % 10 = 6 4
5
It is observed that if we want place all the necessary elements in 55
6
the hash table the size of divisor (m) should be twice as large as 87
7
total number of elements. 37
8 49
9
DOUBLE HASHING
Double hashing is technique in which a second hash function is applied to the key when a
collision occurs. By applying the second hash function we will get the number of positions from
the point of collision to insert.
There are two important rules to be followed for the second function:
 it must never evaluate to zero.
 must make sure that all cells can be probed.
The formula to be used for double hashing is

H1(key) = key mod tablesize


Key
H2(key) = M – (key mod M)
90

where M is a prime number smaller than the size of the table.


22
Consider the following elements to be placed in the hash table of size 10
37, 90, 45, 22, 17, 49, 55
Initially insert the elements using the formula for H1(key).
Insert 37, 90, 45, 22 45

H1(37) = 37 % 10 = 7
H1(90) = 90 % 10 = 0 37
H1(45) = 45 % 10 = 5
H1(22) = 22 % 10 = 2
H1(49) = 49 % 10 = 9 49

108
UNIT -4
Now if 17 to be inserted then
Key
H1(17) = 17 % 10 = 7 90
H2(key) = M – (key % M)
17

Here M is prime number smaller than the size of the table. Prime number 22
smaller than table size 10 is 7

Hence M = 7
45
H2(17) = 7-(17 % 7)
=7–3=4
37
That means we have to insert the element 17 at 4 places from 37. In short we ha v e to take 4
jumps. Therefore the 17 will be placed at index 1.
49
Now to insert number 55
Key
H1(55) = 55 % 10 =5 Collision
90

H2(55) = 7-(55 % 7) 17
=7–6=1 22

That means we have to take one jump from index 5 to place 55.
Finally the hash table will be -
45

55

37

49
Comparison of Quadratic Probing & Double Hashing

The double hashing requires another hash function whose probing efficiency is same as
some another hash function required when handling random collision.
The double hashing is more complex to implement than quadratic probing. The quadratic
probing is fast technique than double hashing.

REHASHING

Rehashing is a technique in which the table is resized, i.e., the size of table is doubled by creating
a new table. It is preferable is the total size of table is a prime number. There are situations in
which the rehashing is required.

 When table is completely full


 With quadratic probing when the table is filled half.
 When insertions fail due to overflow.

109
UNIT -4
In such situations, we have to transfer entries from old table to the new table by re computing
their positions using hash functions.

Consider we have to insert the elements 37, 90, 55, 22, 17, 49, and 87. the table size is 10 and will
use hash function.,

H(key) = key mod tablesize

37 % 10 = 7
90 % 10= 0
55 % 10 = 5
22 % 10 = 2
17 % 10 = 7 Collision solved by linear probing
49 % 10 = 9

Now this table is almost full and if we try to insert more elements collisions will occur and
eventually further insertions will fail. Hence we will rehash by doubling the table size. The old
table size is 10 then we should double this size for new table, that becomes 20. But 20 is not a
prime number, we will prefer to make the table size as 23. And new hash function will be

H(key) key mod 23 0 90


1 11
37 % 23 = 14 2 22
90 % 23 = 21 3
55 % 23 = 9 4
22 % 23 = 22 5 55
17 % 23 = 17 6 87
49 % 23 = 3 7 37
87 % 23 = 18 8 49
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

Now the hash table is sufficiently large to accommodate new insertions.

Advantages:

110
UNIT -4
1. This technique provides the programmer a flexibility to enlarge the table size if required.
2. Only the space gets doubled with simple hash function which avoids occurrence of
collisions.

EXTENSIBLE HASHING

 Extensible hashing is a technique which handles a large amount of data. The data to be
placed in the hash table is by extracting certain number of bits.
 Extensible hashing grow and shrink similar to B-trees.
 In extensible hashing referring the size of directory the elements are to be placed in
buckets. The levels are indicated in parenthesis.

For eg: Directory

0 1
Levels
(0) (1)
001 111
data to be
010
placed in bucket

 The bucket can hold the data of its global depth. If data in bucket is more than global
depth then, split the bucket and double the directory.

Consider we have to insert 1, 4, 5, 7, 8, 10. Assume each page can hold 2 data entries (2 is the
depth).

Step 1: Insert 1, 4
1 = 001
0
4 = 100
(0)
We will examine last bit
001
of data and insert the data
010
in bucket.

Insert 5. The bucket is full. Hence double the directory.

111
21CSC201J
DATA STRUCTURES AND
ALGORITHMS

UNIT-5
Topic : Graph – Graph Traversal
Graph

• GRAPH TERMINOLOGY
• GRAPH TRAVERSAL
Graph

• A graph - an abstract data structure that is used to implement the graph


concept from mathematics.
• A collection of vertices (also called nodes) and edges that connect
these vertices.
• A graph is often viewed as a generalization of the tree structure, where
instead of a having a purely parent-to-child relationship between tree
nodes.
• Any kind of complex relationships between the nodes can be
represented.
Why graphs are useful?

• Graphs are widely used to model any situation where entities or


things are related to each other in pairs; for example, the following
information can be represented by graphs:
• Family trees in which the member nodes have an edge from parent to
each of their children.
• Transportation networks in which nodes are airports, intersections,
ports, etc. The edges can be airline flights, one-way roads, shipping
routes, etc.
Graph : Definition

• A graph G is defined as an ordered set (V, E), where V(G) represent the set of
vertices and E(G) represents the edges that connect the vertices.
• The figure given shows a graph with V(G) = { A, B, C, D and E} and
E(G) = { (A, B), (B, C), (A, D), (B, D), (D, E), (C, E) }.
Note that there are 5 vertices or nodes and 6 edges in the graph.

Definition
A B C

D E
Graph

A graph can be directed (Fig a)or undirected (Fig b).


A B C

Fig:a
D E

A B C

Fig:bDefinition
D E
Graph Terminology:

Adjacent Nodes or Neighbors:


For every edge, e = (u, v) that connects nodes u and v; the nodes u and v
are the end-points and are said to be the adjacent nodes or neighbors.
Degree of a node:
Degree of a node u, deg(u), is the total number of edges containing the
node u. If deg(u) = 0, it means that u does not belong to any edge and
such a node is known as an isolated node.
Regular graph:

Regular graph is a graph where each vertex has the same


number of neighbors.
That is every node has the same degree.
A regular graph with vertices of degree k is called a k-regular
graph or regular graph of degree k.

1 regular graph 2 regular graph


O regular graph
Graph : Path

• Path: A path P, written as P = {v0, v1, v2,….., vn), of length n from a node
u to v is defined as a sequence of (n+1) nodes. Here, u = v0, v = vn and vi-
1 is adjacent to vi for i = 1, 2, 3, …, n.
• Closed path: A path P is known as a closed path if the edge has the same
end-points. That is, if v0 = vn.
• Simple path: A path P is known as a simple path if all the nodes in the
path are distinct with an exception that v0 may be equal to vn. If v0 = vn,
then the path is called a closed simple path.
• Cycle: A closed simple path with length 3 or more is known as a cycle. A
cycle of length k is called a k – cycle.
Graph: path

• Connected graph: A graph in which there exists a path between any two
of its nodes is called a connected graph. That is to say that there are no
isolated nodes in a connected graph. A connected graph that does not have
any cycle is called a tree.
• Complete graph: A graph G is said to be a complete, if all its nodes are
fully connected, that is, there is a path from one node to every other node
in the graph. A complete graph has n(n-1)/2 edges, where n is the number
of nodes in G.
Graph : Path
• Labeled graph or weighted graph: A graph is said to be labeled if every edge in the graph is
assigned some data. In a weighted graph, the edges of the graph are assigned some weight or
length. Weight of the edge, denoted by w(e) is a positive value which indicates the cost of
traversing the edge.
• Multiple edges: Distinct edges which connect the same end points are called multiple edges.
That is, e = {u, v) and e’ = (u, v) are known as multiple edges of G.
• Loop: An edge that has identical end-points is called a loop. That is, e = (u, u).
• Multi- graph: A graph with multiple edges and/or a loop is called a multi-graph.
• Size of the graph: The size of a graph is the total number of edges in it.
(a) Multi-graph (b) Tree (c) Weighted Graph

3 4
e1
A B C
e4 A B C
A B
e2
2
e3
7 1

C e6 D E F
e5 e7 D B
3
Directed Graph

Directed Graph:
• A directed graph G, also known as a digraph, is a graph in which every edge has a
direction assigned to it. An edge of a directed graph is given as an ordered pair (u, v) of
nodes in G. For an edge (u, v)-

• The edge begins at u and terminates at v

• U is known as the origin or initial point of e. Correspondingly, v is known as the destination


or terminal point of e

• U is the predecessor of v. Correspondingly, v is the successor of u nodes u and v are


adjacent to each other.
Terminologies in Directed graph

Terminologies in Directed graph:


• Out-degree of a node: The out degree of a node u, written as outdeg(u), is the number of
edges that originate at u.

• In-degree of a node: The in degree of a node u, written as indeg(u), is the number of edges
that terminate at u.

• Degree of a node: Degree of a node written as deg(u) is equal to the sum of in-degree and
out-degree of that node. Therefore, deg(u) = indeg(u) + outdeg(u)

• Source: A node u is known as a source if it has a positive out-degree but an in-degree = 0.

• Sink: A node u is known as a sink if it has a positive in degree but a zero out-degree.

• Reachability: A node v is said to be reachable from node u, if and only if there exists a
(directed) path from node u to node v.
Terminologies in Directed graph

• Strongly connected directed graph: A digraph is said to be strongly connected if and only if
there exists a path from every pair of nodes in G. That is, if there is a path from node u to v,
then there must be a path from node v to u.
• Unilaterally connected graph: A digraph is said to be unilaterally connected if there exists a
path from any pair of nodes u, v in G such that there is a path from u to v or a path from v to
u but not both.
• Parallel/Multiple edges: Distinct edges which connect the same end points are called
multiple edges. That is, e = {u, v) and e’ = (u, v) are known as multiple edges of G.
• Simple directed graph: A directed graph G is said to be a simple directed graph if and only
if it has no parallel edges. However, a simple directed graph may contain cycle with an
exception that it cannot have more than one loop at a given node
Directed Graph

Definition
For a directed graph G = (V,E), where V is the set of vertices and E is the set of edges, the transitive closure
of G is a graph G* = (V,E*). In G*, for every vertex pair v, w in V there is an edge (v, w) in E* if and only if
there is a valid path from v to w in G.

• A binary relation indicates only whether the node A is connected to node B, whether node B is connected
to node C, etc. But once the transitive closure is constructed as shown in Fig. we can easily determine in
O(1) time whether node E is reachable from node A or not.
Graph Representation:

Graph Representation:
• There are three common ways of storing graphs in the computer’s memory.
• They are:
• Sequential representation by using an adjacency matrix.
• Linked representation by using an adjacency list that stores the neighbors of a node
using a linked list.
• Adjacency multi-list which is an extension of linked representation.
Adjacency Matrix Representation:
Adjacency Matrix Representation:

• An adjacency matrix is used to represent which nodes are adjacent to one another. By definition, two nodes are
said to be adjacent if there is an edge connecting them.
• In this representation, graph can be represented using a matrix of size V*V.
• No matter how few edges the graph has, the matrix has O(n^2) in memory.

• Graph is represented using a matrix of size total number of vertices by a total number of vertices
Adjacency Matrix Representation:
Adjacency Matrix Representation:

Adjacency List Representation:


• An adjacency list is another way in which graphs can be represented in the computer’s memory.
• This structure consists of a list of all nodes in G. Furthermore, every node is in turn linked to its own list that
contains the names of all other nodes that are adjacent to it.
• The key advantages of using an adjacency list are:
• It is easy to follow and clearly shows the adjacent nodes of a particular node.
• It is often used for storing graphs that have a small-to-moderate number of edges. That is, an adjacency list
is preferred for representing sparse graphs in the computer’s memory; otherwise, an adjacency matrix is a
good choice.
• Adding new nodes in G is easy and straightforward when G is represented using an adjacency list. Adding
new nodes in an adjacency matrix is a difficult task, as the size of the matrix needs to be changed and
existing nodes may have to be reordered.
Adjacency Matrix Representation:

Adjacency List Representation: every vertex of a graph contains list of its adjacent vertices.
Adjacency Matrix Representation:

Adjacency List Representation:


Adjacency Multi-list Representation:

Adjacency Multi-list Representation:


• Graphs can also be represented using multi-lists which can be said to be modified version of
adjacency lists.
• Adjacency multi-list is an edge-based rather than a vertex-based representation of graphs.
• A multi-list representation basically consists of two parts—
• a directory of nodes’ information and a set of linked lists storing information about
edges.
• While there is a single entry for each node in the node directory, every node, on the other
hand, appears in two adjacency lists (one for the node at each end of the edge).
• For example, the directory entry for node i points to the adjacency list for node i. This
means that the nodes are shared among several lists.
Adjacency Multi-list Representation:

In a multi-list representation, the information about an edge (vi, vj) of an undirected graph can
be stored using the following attributes:
M: A single bit field to indicate whether the edge has been examined or not.
vi: A vertex in the graph that is connected to vertex vj by an edge.
vj: A vertex in the graph that is connected to vertex vi by an edge.
Link i for vi: A link that points to another node that has an edge incident on vi.
Link j for vi: A link that points to another node that has an edge incident on vj.
Adjacency Multi-list Representation:

Adjacency Multi-list Representation:


Consider the undirected graph given in Fig. The adjacency multi-list for the graph can be given as:
GRAPH TRAVERSAL ALGORITHMS:

GRAPH TRAVERSAL ALGORITHMS:


• This technique is used for searching a vertex in a graph.
• It is also used to decide the order of vertices to be visit in the search process.
• There are two standard methods of graph traversal:
1. Breadth-first search
2. Depth-first search
• While breadth-first search uses a queue as an auxiliary data structure to store nodes for
further processing.
• The depth-first search scheme uses a stack.
Breadth-First Search :

Breadth-First Search :
• It is a graph search algorithm that begins at the root node and explores all the
neighboring nodes.
• Then for each of those nearest nodes, the algorithm explores their unexplored neighbor
nodes, and so on, until it finds the goal.
• This technique is used for searching a vertex in a graph.
• It produces a spanning tree as a final result.
• Spanning tree is a graph without any loops.
• Here we use Queue data structure with maximum size of total number of vertices in the
graph.
Breadth-First Search Algorithm:
STEPS:
Step 1 - Define a Queue of size total number of vertices in the graph.
Step 2 - Select any vertex as starting point for traversal. Visit that vertex and insert it into the
Queue.
Step 3 - Visit all the non-visited adjacent vertices of the vertex which is at front of the Queue
and insert them into the Queue.
Step 4 - When there is no new vertex to be visited from the vertex which is at front of the
Queue then delete that vertex.
Step 5 - Repeat steps 3 and 4 until queue becomes empty.
Step 6 - When queue becomes empty, then produce final spanning tree by removing unused
edges from the graph
Breadth-First Search Algorithm:
Breadth-First Search Algorithm:
Breadth-First Search Algorithm:
Breadth-First Search Algorithm:
Breadth-First Search Algorithm:
Features of Breadth-First Search Algorithm:
• Space complexity
• In the breadth-first search algorithm, all the nodes at a particular level must be saved until their child
nodes in the next level have been generated.
• The space complexity is therefore proportional to the number of nodes at the deepest level of the graph.
Given a graph with branching factor b (number of children at each node) and depth d, the asymptotic
space complexity is the number of nodes at the deepest level O(bd).
• Time complexity
• In the worst case, breadth-first search has to traverse through all paths to all possible nodes, thus the time
complexity of this algorithm asymptotically approaches O(bd).
• However, the time complexity can also be expressed as O( | E | + | V | ), since every vertex and every
edge will be explored in the worst case.
• Completeness
• Breadth-first search is said to be a complete algorithm because if there is a solution,
breadth-first search will find it regardless of the kind of graph.
• But in case of an infinite graph where there is no possible solution, it will diverge.
• Optimality
• Breadth-first search is optimal for a graph that has edges of equal length, since it
always returns the result with the fewest edges between the start node and the goal
node.
• But generally, in real-world applications, we have weighted graphs that have costs
associated with each edge, so the goal next to the start does not have to be the cheapest
goal available.
Applications of Breadth-First Search Algorithm:
• Breadth-first search can be used to solve many problems such as:
• Finding all connected components in a graph G.
• Finding all nodes within an individual connected component.
• Finding the shortest path between two nodes, u and v, of an unweighted graph.
• Finding the shortest path between two nodes, u and v, of a weighted graph.
Depth-first Search :

Depth-first Search :
• The depth-first search algorithm progresses by expanding the starting node of G and then
going deeper and deeper until the goal node is found, or until a node that has no children is
encountered. When a dead-end is reached, the algorithm backtracks, returning to the
most recent node that has not been completely explored.
• In other words, depth-first search begins at a starting node A which becomes the current
node. Then, it examines each node N along a path P which begins at A. That is, we process a
neighbor of A, then a neighbor of neighbor of A, and so on.
Depth-first Search :
• During the execution of the algorithm, if we reach a path that has a node N that has
already been processed, then we backtrack to the current node. Otherwise, the unvisited
(unprocessed) node becomes the current node.
• This technique is used for searching a vertex in a graph.
• It produces a spanning tree as a final result.
• Spanning tree is a graph without any loops.
• Here we use Stack data structure with maximum size of total number of vertices in the
graph.
Depth-first Search Algorithm:
• Step 1 - Define a Stack of size total number of vertices in the graph.
• Step 2 - Select any vertex as starting point for traversal. Visit that vertex and push it
on to the Stack.
• Step 3 - Visit any one of the non-visited adjacent vertices of a vertex which is at the
top of stack and push it on to the stack.
• Step 4 - Repeat step 3 until there is no new vertex to be visited from the vertex which is
at the top of the stack.
• Step 5 - When there is no new vertex to visit then use back tracking and pop one
vertex from the stack.
• Step 6 - Repeat steps 3, 4 and 5 until stack becomes Empty.
• Step 7 - When stack becomes Empty, then produce final spanning tree by removing
unused edges from the graph
Depth-first Search Algorithm:
Depth-first Search Algorithm:
Depth-first Search Algorithm:
Depth-first Search Algorithm:
Depth-first Search Algorithm:
Depth-first Search Algorithm:
Depth-first Search Algorithm:
Depth-first Search Algorithm:
Depth-first Search Algorithm:
Features of Depth-First Search Algorithm
Space complexity The space complexity of a depth-first search is lower than that of a
breadthfirst
search.
Time complexity The time complexity of a depth-first search is proportional to the number of
vertices plus the number of edges in the graphs that are traversed. The time complexity can be
given as (O(|V| + |E|)).
Completeness Depth-first search is said to be a complete algorithm. If there is a solution,
depthfirst search will find it regardless of the kind of graph. But in case of an infinite graph,
where there is no possible solution, it will diverge.
Applications of Depth-First Search Algorithm
Depth-first search is useful for:
• Finding a path between two specified nodes, u and v, of an unweighted graph.
• Finding a path between two specified nodes, u and v, of a weighted graph.
• Finding whether a graph is connected or not.
• Computing the spanning tree of a connected graph.
21CSC201J
DATA STRUCTURES AND
ALGORITHMS

UNIT-5
Topic : Topological Sort
Topological Sort
Topological Sort
Topological Sort
Topological Sort
Topological Sort
Topological Sort
Topological Sort
Topological Sort
Topological Sort
21CSC201J
DATA STRUCTURES AND
ALGORITHMS

UNIT-5
Topic : Minimum Spanning Tree
Spanning Tree
• A spanning tree is defined as a subgraph of a connected, undirected
graph that includes all the vertices of the graph.
• A spanning tree is a subset of Graph G, which has all the vertices
covered with minimum possible no of edges.
• A spanning tree does not have cycles (acyclic) and it cannot be
disconnected.
Spanning Tree : properties
• Graph can be represented as G(V, E), 'V' - no of vertices, and 'E' - no of
edges.
• The spanning tree of the graph would be represented as G`(V`, E`).
• V` = V , no of vertices in the spanning tree would be the same as the
no of vertices in the graph.
• E` € E , no of edges in the spanning tree is the subset of the no of
edges in the graph.
Spanning Tree :properties
• E` = |V| - 1 , no of edges in the spanning tree would be equal to the
no of edges minus 1.
• The spanning tree should not be disconnected
• The spanning tree should be acyclic
• The total cost (or weight) of the spanning tree is defined as the sum
of the edge weights of all the edges .
Minimum Spanning Tree
• A minimum spanning tree (MST) is defined as a spanning tree that
has the minimum weight among all the possible spanning trees.
• The minimum spanning tree is a spanning tree whose sum of the
edges is minimum.
• There can be many possible MSTs for a graph.
Minimum Spanning Tree
• A minimum spanning tree (MST) is defined as a spanning tree that
has the minimum weight among all the possible spanning trees.
• The minimum spanning tree is a spanning tree whose sum of the
edges is minimum.
• There can be many possible MSTs for a graph.
Minimum Spanning Tree : Applications
• Communication network & laying with min cost.
• Construct highway roads or railroads spanning several cities .
• Designing Local Area Networks.
• Laying pipelines, Electric Power lines , Telephone lines & Sewage lines.
Minimum Spanning Tree
• Finds the minimum spanning tree from a connected, undirected
graph. This is a greedy algorithm.
Minimum Spanning Tree: Kruskal's Algorithm
• Two most important spanning tree algorithms are −
• Kruskal's Algorithm
• Prim's Algorithm
Minimum Spanning Tree : Kruskal's Algorithm
• Finds the minimum spanning tree from a connected, undirected
graph.
• This is a greedy algorithm.
• The Greedy Choice is to put the smallest weight edge that does not
because a cycle in the MST constructed so far.

• Remove self loop & parallel edges first.


Minimum Spanning Tree : Kruskal's Algorithm
Steps to generate MST Remove self loop & parallel edges
• Step 1: Sort all edges in increasing order of their edge weights.
• Step 2: Pick the smallest edge.
• Step 3: Check if the new edge creates a cycle or loop in a spanning
tree.
• Step 4: If it doesn’t form the cycle, then include that edge in MST.
Otherwise, discard it.
• Step 5: Repeat from step 2 until it includes |V| - 1 edges in MST.
Minimum Spanning Tree : Kruskal's Algorithm
MST - Kruskal’s algorithm
1. A ← ∅
2. for each vertex v ∈ V [G]
3. do MAKE - SET (v)
4. sort the edges of E into non decreasing order by weight w
5. for each edge (u, v) ∈ E, taken in non decreasing order by weight
6. do if FIND-SET (μ) ≠ if FIND-SET (v)
7. then A ← A ∪ {(u, v)}
8. UNION (u, v)
9. return A
Minimum Spanning Tree : Kruskal's Algorithm
MST - Kruskal’s algorithm 9 vertices & 12 edges. So MST formed (9-1) = 8 edges
Minimum Spanning Tree : Kruskal's Algorithm
•a
Minimum Spanning Tree : Kruskal's Algorithm
•a
Find MST using Kruskal’s Algortihm

3 4
2 3 4
4 5
2
9
5 6
1
1
12
8 10
7 8 9
7

The graph contains 9 vertices and 11 edges. So, the minimum spanning tree formed will be having (9 – 1) = 8
edges.
Sort the weights of the graph
Weight Source Destination
1 8 5
2 3 5
3 2 3
4 1 2
4 3 4
5 4 6
7 7 8
8 1 7
9 5 6
10 8 9
12 9 6
Now pick all edges one by one from sorted list of edges

5
Weight Source Destinati 1
on

1 8 5
8
2 3 5
3 2 3
4 1 2
4 3 4
5 4 6
7 7 8
8 1 7
9 5 6
10 8 9
12 9 6
3

2
5
1
Weight Source Destinati
on

1 8 5 8
2 3 5
3 2 3
4 1 2
4 3 4
5 4 6
7 7 8
8 1 7
9 5 6
10 8 9
12 9 6
3
2 3

2
5
Weight Source Destinati
on 1
1 8 5
2 3 5 8
3 2 3
4 1 2
4 3 4
5 4 6
7 7 8
8 1 7
9 5 6
10 8 9
12 9 6
3
2 3
4
2
1 5
Weight Source Destinati
on 1
1 8 5
2 3 5
8
3 2 3
4 1 2
4 3 4
5 4 6
7 7 8
8 1 7
9 5 6
10 8 9
12 9 6
3 4
2 3 4
4
2
1 5
Weight Source Destinati
on
1
1 8 5
8
2 3 5
3 2 3
4 1 2
4 3 4
5 4 6
7 7 8
8 1 7
9 5 6
10 8 9
12 9 6
3 4
2 3 4
5
4
2
6
1 5
1
Weight Source Destinati
on 8
1 8 5
2 3 5
3 2 3
4 1 2
4 3 4
5 4 6
7 7 8
8 1 7
9 5 6
10 8 9
12 9 6
3 4
2 3 4
5
4
2
6
1 5
1

Weight Source Destinati


on
7 8
7
1 8 5
2 3 5
3 2 3
4 1 2
4 3 4
5 4 6
7 7 8
8 1 7
9 5 6
10 8 9
12 9 6
3
2 3 4
5
4 4
2
6
1 5
1
Weight Source Destinati 8
on 7 8
7
1 8 5
2 3 5
3 2 3
4 1 2
4 3 4 Cycle is formed because of weight 8. hence it is discarded.
5 4 6
7 7 8
8 1 7
9 5 6
10 8 9
12 9 6
3 4
2 3 4
5
4
2
9 6
1 5
1

7 8
Weight Source Destinati 7
on

1 8 5
2 3 5
3 2 3
4 1 2
4 3 4 Cycle is formed because of weight 9. hence it is discarded.
5 4 6
7 7 8
8 1 7
9 5 6
10 8 9
12 9 6
3 4
2 3 4
5
4
2
6
1 5
1
10
7 8 9
7
Weight Source Destinati
on

1 8 5
2 3 5
3 2 3
4 1 2
4 3 4
Cycle is formed because of weight 12. hence it is discarded.
5 4 6
7 7 8
8 1 7
9 5 6
10 8 9
12 9 6
3 4
2 3 4
5
4
2
6
1 5
1
12
10
7 8 9
7
Weight Source Destinati
on

1 8 5
2 3 5
3 2 3
4 1 2
4 3 4
Cycle is formed because of weight 12. hence it is discarded.
5 4 6
7 7 8
8 1 7
9 5 6
10 8 9
12 9 6
3 4
2 3 4
5
4
2
6
1 5
1
10
7 8 9
7

Minimum Spanning tree generated by Kruskal’s algorithm


Minimum Spanning Tree : Prim's Algorithm
• Prim's algorithm begins with a single arbitrary node and considers all
the edges and picks the minimum weight edge, moves the other
endpoint of edge.
• Edges with the lowest weights are chosen should not generate cycles.
• This also greedy algorithm.
Minimum Spanning Tree : Prim's Algorithm
Steps for finding MST : Remove self loop & parallel edges
Step 1: Choose any vertex as a starting vertex.
Step 2: Pick an edge connecting adjacent vertex to visited vertex having
the minimum edge weight.
Step 3: Add the selected edge to MST only if it doesn't form any closed
cycle.
Step 4: Keep repeating steps 2 and 3 until the any outer vertices exist.
Step 5: End.
Minimum Spanning Tree : Prim's Algorithm
• Find MST using Prim’s Algorithm
Minimum Spanning Tree : Prim's Algorithm
a
• as
Minimum Spanning Tree : Prim's Algorithm
a
• as
Minimum Spanning Tree : Prim's Algorithm
a
• as
Minimum Spanning Tree : Prim's Algorithm
a
• as
Find MST using Prim’s Algortihm

3 4
2 3 4
4 5
2
9
5 6
1
1
12
8 10
7 8 9
7
Dijkstra's Shortest Path
Algorithm - A Detailed and
Visual Introduction
Welcome! If you've always wanted to learn and understand Dijkstra's
algorithm, then this article is for you. You will see how it works behind
the scenes with a step-by-step graphical explanation.

You will learn:

Basic Graph Concepts (a quick review).


What Dijkstra's Algorithm is used for.
How it works behind the scenes with a step-by-step example.

Let's begin. ✨
🔹 Introduction to Graphs
Let's start with a brief introduction to graphs.

Basic Concepts
Graphs are data structures used to represent "connections" between
pairs of elements.

These elements are called nodes. They represent real-life


objects, persons, or entities.
The connections between nodes are called edges.

This is a graphical representation of a graph:


Nodes are represented with colored circles and edges are represented
with lines that connect these circles.

💡 Tip: Two nodes are connected if there is an edge between them.

Applications
Graphs are directly applicable to real-world scenarios. For example, we
could use graphs to model a transportation network where nodes would
represent facilities that send or receive products and edges would
represent roads or paths that connect them (see below).

Network represented with a graph


Types of Graphs
Graphs can be:

Undirected: if for every pair of connected nodes, you can go


from one node to the other in both directions.
Directed: if for every pair of connected nodes, you can only go
from one node to another in a specific direction. We use arrows
instead of simple lines to represent directed edges.

💡 Tip: in this article, we will work with undirected graphs.

Weighted Graphs
A weight graph is a graph whose edges have a "weight" or "cost". The
weight of an edge can represent distance, time, or anything that models
the "connection" between the pair of nodes it connects.

For example, in the weighted graph below you can see a blue number
next to each edge. This number is used to represent the weight of the
corresponding edge.
💡 Tip: These weights are essential for Dijkstra's Algorithm. You will see
why in just a moment.

🔸 Introduction to Dijkstra's Algorithm


Now that you know the basic concepts of graphs, let's start diving into
this amazing algorithm.

Purpose and Use Cases


History
Basics of the Algorithm
Requirements

Purpose and Use Cases


With Dijkstra's Algorithm, you can find the shortest path between nodes
in a graph. Particularly, you can find the shortest path from a node
(called the "source node") to all other nodes in the graph, producing a
shortest-path tree.

This algorithm is used in GPS devices to find the shortest path between
the current location and the destination. It has broad applications in
industry, specially in domains that require modeling networks.

History
This algorithm was created and published by Dr. Edsger W. Dijkstra, a
brilliant Dutch computer scientist and software engineer.

In 1959, he published a 3-page article titled "A note on two problems in


connexion with graphs" where he explained his new algorithm.

Dr. Edsger Dijkstra at ETH Zurich in 1994 (image by Andreas F. Borchert)

During an interview in 2001, Dr. Dijkstra revealed how and why he


designed the algorithm:

What’s the shortest way to travel from Rotterdam to Groningen? It


is the algorithm for the shortest path, which I designed in about 20
minutes. One morning I was shopping in Amsterdam with my young
fiancée, and tired, we sat down on the café terrace to drink a cup of
coffee and I was just thinking about whether I could do this, and I
then designed the algorithm for the shortest path. As I said, it was a
20-minute invention. In fact, it was published in 1959, three years
later. The publication is still quite nice. One of the reasons that it is
so nice was that I designed it without pencil and paper. Without
pencil and paper you are almost forced to avoid all avoidable
complexities. Eventually that algorithm became, to my great
amazement, one of the cornerstones of my fame. — As quoted in the
article Edsger W. Dijkstra from An interview with Edsger W.
Dijkstra.

⭐ Unbelievable, right? In just 20 minutes, Dr. Dijkstra designed one of


the most famous algorithms in the history of Computer Science.

Basics of Dijkstra's Algorithm


Dijkstra's Algorithm basically starts at the node that you choose
(the source node) and it analyzes the graph to find the shortest
path between that node and all the other nodes in the graph.
The algorithm keeps track of the currently known shortest
distance from each node to the source node and it updates these
values if it finds a shorter path.
Once the algorithm has found the shortest path between the
source node and another node, that node is marked as "visited"
and added to the path.
The process continues until all the nodes in the graph have been
added to the path. This way, we have a path that connects the
source node to all other nodes following the shortest path
possible to reach each node.

Requirements
Dijkstra's Algorithm can only work with graphs that have positive
weights. This is because, during the process, the weights of the edges
have to be added to find the shortest path.

If there is a negative weight in the graph, then the algorithm will not
work properly. Once a node has been marked as "visited", the current
path to that node is marked as the shortest path to reach that node. And
negative weights can alter this if the total weight can be decremented
after this step has occurred.
🔹 Example of Dijkstra's Algorithm
Now that you know more about this algorithm, let's see how it works
behind the scenes with a a step-by-step example.

We have this graph:

The algorithm will generate the shortest path from node 0 to all the other
nodes in the graph.

💡 Tip: For this graph, we will assume that the weight of the edges
represents the distance between two nodes.

We will have the shortest path from node 0 to node 1, from node 0 to
node 2, from node 0 to node 3, and so on for every node in the graph.

Initially, we have this list of distances (please see the list below):

The distance from the source node to itself is 0. For this


example, the source node will be node 0 but it can be any node
that you choose.
The distance from the source node to all other nodes has not
been determined yet, so we use the infinity symbol to represent
this initially.
We also have this list (see below) to keep track of the nodes that have not
been visited yet (nodes that have not been included in the path):

💡 Tip: Remember that the algorithm is completed once all nodes have
been added to the path.

Since we are choosing to start at node 0, we can mark this node as


visited. Equivalently, we cross it off from the list of unvisited nodes and
add a red border to the corresponding node in diagram:
Now we need to start checking the distance from node 0 to its adjacent
nodes. As you can see, these are nodes 1 and 2 (see the red edges):

💡 Tip: This doesn't mean that we are immediately adding the two
adjacent nodes to the shortest path. Before adding a node to this path,
we need to check if we have found the shortest path to reach it. We are
simply making an initial examination process to see the options
available.

We need to update the distances from node 0 to node 1 and node 2 with
the weights of the edges that connect them to node 0 (the source node).
These weights are 2 and 6, respectively:
After updating the distances of the adjacent nodes, we need to:

Select the node that is closest to the source node based on the
current known distances.
Mark it as visited.
Add it to the path.

If we check the list of distances, we can see that node 1 has the shortest
distance to the source node (a distance of 2), so we add it to the path.

In the diagram, we can represent this with a red edge:

We mark it with a red square in the list to represent that it has been
"visited" and that we have found the shortest path to this node:
We cross it off from the list of unvisited nodes:

Now we need to analyze the new adjacent nodes to find the shortest path
to reach them. We will only analyze the nodes that are adjacent to the
nodes that are already part of the shortest path (the path marked with
red edges).

Node 3 and node 2 are both adjacent to nodes that are already in the path
because they are directly connected to node 1 and node 0, respectively, as
you can see below. These are the nodes that we will analyze in the next
step.
Since we already have the distance from the source node to node 2
written down in our list, we don't need to update the distance this time.
We only need to update the distance from the source node to the new
adjacent node (node 3):

This distance is 7. Let's see why.

To find the distance from the source node to another node (in this case,
node 3), we add the weights of all the edges that form the shortest path
to reach that node:

For node 3: the total distance is 7 because we add the weights


of the edges that form the path 0 -> 1 -> 3 (2 for the edge 0 -> 1
and 5 for the edge 1 -> 3).
Now that we have the distance to the adjacent nodes, we have to choose
which node will be added to the path. We must select the unvisited node
with the shortest (currently known) distance to the source node.

From the list of distances, we can immediately detect that this is node 2
with distance 6:

We add it to the path graphically with a red border around the node and
a red edge:

We also mark it as visited by adding a small red square in the list of


distances and crossing it off from the list of unvisited nodes:
Now we need to repeat the process to find the shortest path from the
source node to the new adjacent node, which is node 3.

You can see that we have two possible paths 0 -> 1 -> 3 or 0 -> 2 -> 3.
Let's see how we can decide which one is the shortest path.

Node 3 already has a distance in the list that was recorded previously (7,
see the list below). This distance was the result of a previous step, where
we added the weights 5 and 2 of the two edges that we needed to cross to
follow the path 0 -> 1 -> 3.

But now we have another alternative. If we choose to follow the path 0 ->
2 -> 3, we would need to follow two edges 0 -> 2 and 2 -> 3 with weights
6 and 8, respectively, which represents a total distance of 14.

Clearly, the first (existing) distance is shorter (7 vs. 14), so we will


choose to keep the original path 0 -> 1 -> 3. We only update the
distance if the new path is shorter.

Therefore, we add this node to the path using the first alternative: 0 -> 1
-> 3.

We mark this node as visited and cross it off from the list of unvisited
nodes:
Now we repeat the process again.

We need to check the new adjacent nodes that we have not visited so far.
This time, these nodes are node 4 and node 5 since they are adjacent to
node 3.

We update the distances of these nodes to the source node, always trying
to find a shorter path, if possible:

For node 4: the distance is 17 from the path 0 -> 1 -> 3 -> 4.
For node 5: the distance is 22 from the path 0 -> 1 -> 3 -> 5.
💡 Tip: Notice that we can only consider extending the shortest path
(marked in red). We cannot consider paths that will take us through
edges that have not been added to the shortest path (for example, we
cannot form a path that goes through the edge 2 -> 3).

We need to choose which unvisited node will be marked as visited now.


In this case, it's node 4 because it has the shortest distance in the list of
distances. We add it graphically in the diagram:

We also mark it as "visited" by adding a small red square in the list:


And we cross it off from the list of unvisited nodes:

And we repeat the process again. We check the adjacent nodes: node 5
and node 6. We need to analyze each possible path that we can follow to
reach them from nodes that have already been marked as visited and
added to the path.

For node 5:

The first option is to follow the path 0 -> 1 -> 3 -> 5, which has
a distance of 22 from the source node (2 + 5 + 15). This distance
was already recorded in the list of distances in a previous step.
The second option would be to follow the path 0 -> 1 -> 3 -> 4 -
> 5, which has a distance of 23 from the source node (2 + 5 + 10
+ 6).

Clearly, the first path is shorter, so we choose it for node 5.

For node 6:

The path available is 0 -> 1 -> 3 -> 4 -> 6, which has a distance
of 19 from the source node (2 + 5 + 10 + 2).

We mark the node with the shortest (currently known) distance as


visited. In this case, node 6.

And we cross it off from the list of unvisited nodes:

Now we have this path (marked in red):

Only one node has not been visited yet, node 5. Let's see how we can
include it in the path.

There are three different paths that we can take to reach node 5 from the
nodes that have been added to the path:

Option 1: 0 -> 1 -> 3 -> 5 with a distance of 22 (2 + 5 + 15).


Option 2: 0 -> 1 -> 3 -> 4 -> 5 with a distance of 23 (2 + 5 + 10
+ 6).
Option 3: 0 -> 1 -> 3 -> 4 -> 6 -> 5 with a distance of 25 (2 + 5
+ 10 + 2 + 6).
We select the shortest path: 0 -> 1 -> 3 -> 5 with a distance of 22.

We mark the node as visited and cross it off from the list of unvisited
nodes:

And voilà! We have the final result with the shortest path from node 0 to
each node in the graph.

In the diagram, the red lines mark the edges that belong to the shortest
path. You need to follow these edges to follow the shortest path to reach
a given node in the graph starting from node 0.

For example, if you want to reach node 6 starting from node 0, you just
need to follow the red edges and you will be following the shortest path 0
-> 1 -> 3 -> 4 - > 6 automatically.

🔸 In Summary
Graphs are used to model connections between objects, people,
or entities. They have two main elements: nodes and edges.
Nodes represent objects and edges represent the connections
between these objects.
Dijkstra's Algorithm finds the shortest path between a given
node (which is called the "source node") and all other nodes in a
graph.
This algorithm uses the weights of the edges to find the path
that minimizes the total distance (weight) between the source
node and all other nodes.
I really hope you liked my article and found it helpful. Now you know
how Dijkstra's Algorithm works behind the scenes. Follow me on Twitter
@EstefaniaCassN and check out my online courses.

You might also like

pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy