DSA Unit 3, 4, 5 (1)
DSA Unit 3, 4, 5 (1)
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()
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 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.
• 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:
• Backtracking
• Delimiter Checking
• Reverse a Data
Example: A + (B - C)
Notations for Arithmetic Expression
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*
Delimiter Checking
• 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
{ ( 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.
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:
• 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
• In which operators are written infix-style between the operands they act on.
Example: A + B
Infix to Postfix Conversion
Postfix Notation
• For example, the infix expression A + B will be written as AB+ in its Postfix
Notation.
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
Step 3
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
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
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.
Example
Example
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
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
UNIT-3
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
Time and Space complexity for evaluating postfix expression using stack are
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.
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…)
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
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
UNIT-3
fact(4);
A B c
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 :
• 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
UNIT-3
Topic : Queue Implementation
Ways to implement the Queue ADT
} // display
.
Disadvantage of Array Implementation of 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
• 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
->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
• 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
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.
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
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.
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
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
Step 2: END
EXAMPLE : Inserting nodes with values 55 in the given binary search tree
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.
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.
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
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
• 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:
• 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
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.
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.
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
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-
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-
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-
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-
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-
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-
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-
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-
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.
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;
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.
For example:
105
UNIT -4
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.
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
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.
for eg; If we have to insert following elements in the hash table with table size 10:
Consider i = 0 then
(17 + 02) % 10 = 7
107
UNIT -4
(17 + 12) % 10 = 8, when i =1
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.
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.,
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
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.
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.
111
21CSC201J
DATA STRUCTURES AND
ALGORITHMS
UNIT-5
Topic : Graph – Graph Traversal
Graph
• GRAPH TERMINOLOGY
• GRAPH TRAVERSAL
Graph
• 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
Fig:a
D E
A B C
Fig:bDefinition
D E
Graph Terminology:
• 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)-
• 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)
• 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: every vertex of a graph contains list of its adjacent vertices.
Adjacency Matrix 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:
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.
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
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
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.
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.
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).
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.
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.
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.
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):
💡 Tip: Remember that the algorithm is completed once all nodes have
been added to the path.
💡 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.
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):
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:
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:
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.
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).
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).
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).
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:
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.