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

Graph & Hashing

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

Graph & Hashing

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

Data Structures &

Algorithms
Parenthesis Checker (Bracket Matching)

 Parenthesis Checker is a program that checks whether a mathematical


expression is properly parenthesized.

 We will considers three sets of grouping symbols :


The standard parentheses “( )”
Braces “{ }”
Brackets “[ ]”

 For input expression, it verifies that for each left parentheses, brace, or
brackets, there is a corresponding closing symbol and the symbols are
appropriately nested.
Examples:

() is proper
({}[]) is proper
( ){ [ ] is not proper
({)} is not proper
)([ ] is not proper
([])) is not proper
Approach

 Whenever a left parenthesis is encountered, it is pushed in the stack

 Whenever a right parenthesis is encountered, pop from stack and


check if the parentheses match
ParenthesisChecker ( exp )

Here exp is character string representing an arithmetic expression comprising all


types of bracketing symbols.

Begin

read : exp
create empty stack
for each character c in exp
if ( current character is a left symbol ) then
Push the character onto stack
else if ( current character is a left symbol ) then
if ( stack is empty ) then
Print : “Error : No matching open symbol”
else
Pop a symbol s from the stack
if ( s doesn’t correspond to c ) then
Print : “Error : Incorrect nesting of symbols”
endif
endif
endif
endfor

if ( stack is not empty ) then


Print : “Error: Missing closing symbol(s)”
else
Print : “Input expression is OK”
endif
End
Evaluating Postfix Expression

 Suppose we have following Postfix Expression


7 5 – 9 2 / *

Character Scanned Stack


7 7
5 75
- 2
9 29
2 292
/ 2 4.5
* 9
End of Expression 9
Algorithm
EvaluatePostfixExpression ( p, result )
Here p is the arithmetic expression in postfix notation. This algorithm evaluates this expression and
returns the value of the expression through variable result.
Begin
Create Empty Stack
while ( not end of expression ) do
if ( elements is operand ) then
Push element onto stack
else
Pop two elements and let first one is a and the second one is b
Evaluate b © a, let its value be c, where © is an operator
Push c onto stack
endif
endwhile
Pop stack and assign this value to parameter result.
End
Tower of Hanoi
 This is a Recursive problem.
 This is actually a game consisting of three pins and a set of disks of different
sizes.
 The game start with the disks stacked in the decreasing size on the first pin.

 The aim of this game is to move the disks to another pin with the following
constraints:
 Only one disk can be moved at a time, and that too the top disk.
 No disk can be placed on the top of smaller disk.

 All the three pins can be used, and a disk can be moved directly from any
pin to other one.
 Here we have to design a general algorithm for solving this problem with n
disks from pin A to Pin C using third pin B.
Example : Given One Disk Only

 In this case, which is the base case, the disk can be directly moved from
pin A to pin C.

 Move the top disk from A to C


Example : Given Two Disk
Example : Given Two Disk

 Move Disk 1 from Pin A to Pin B


Example : Given Two Disk

 Move Disk 2 from Pin A to Pin C


Example : Given Two Disk

 Move Disk 1 from Pin B to Pin C


Example : Given Three Disk
Example : Given Three Disk

 Move disk 1 from pin A to Pin C


Example : Given Three Disk

 Move disk 2 from pin A to Pin B


Example : Given Three Disk

 Move disk 1 from pin C to Pin B


Example : Given Three Disk

 Move disk 3 from pin A to Pin C


Example : Given Three Disk

 Move disk 1 from pin B to Pin A


Example : Given Three Disk

 Move disk 2 from pin B to Pin C


Example : Given Three Disk

 Move disk 1 from pin A to Pin C


 Having examined the above cases with two or three disks, the following
observations can be made:

 In order to move the largest disk from pin A to Pin C, we must move the smaller
disks that are on the top to pin B.

 Then move the largest disk from Pin A to Pin C.

 Once the largest disk has been moved, we must move, in the similar
manner, all the smaller disks to pin C.
Algorithm

TowerofHanoi ( from, to, other, n )


Begin
if ( n == 1 ) then
print : “Move disk from” from “to” to
else
TowerofHanoi ( from, other, to, n – 1 )
TowerofHanoi ( from, to, other, 1 )
TowerofHanoi (other, to, from, n – 1 )
endif
End
Queue
 It is an ordered group of homogeneous items of elements.
 It is a Linear list in which insertions can take place at one end of the list ,
called the rear of the list, and deletions can take place only at other end,
called the front of the list.
 The element added first is also removed first so it is also called First-In-First-
Out (FIFO) list.
Conceptual View of a Queue

Adding an
element
Front of queue

New element is
added to the rear
of the queue
Conceptual View of a Queue

Removing an element

New front element of queue

Element is removed
from the front of the
queue
Example
0 1 2 3 4 5 6 7 8

An Empty Queue

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

Queue after inserting elements 5, 7, 12, 8 and 9 in order

front rear

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

Queue after deleting elements 5, 7 from queue

front rear
Operations on Queues

 CreateQueue(q) – to create q as an empty queue.


 Enqueue(q,i) – to insert (add) element i in a queue q.
 Dequeue(q) – to access and remove an element of queue q.
 Peek(q) – to access the first element of the queue q without removing it.
 IfFull(q) – to check whether the queue q is full.
 IsEmpty(q) – to check whether the queue q is empty.
Representing a Queue using an Array

 To implement queue we need two variables, called front and rear.


 front holds the index of the first element.
 rear holds the index of the last element.
 Here we also require an array to hold the elements of the queue.
 Declaration of Queue :
#define CAPACITY 10
int front, rear;
int Queue[CAPACITY];
Operation : Creating an empty Linear
Queue
 Before we can use a queue, it must be created / initialized.
 We craete an empty queue by assigning value -1 to front and rear
variables.

createQueue()
This algorithm craetes an empty queue.
Begin
set front = rear = -1
End
Testing a Linear Queue for Underflow
 Before we remove an an element from a queue, it is necessary to test
whether the queue is empty or not.
 This condition will be checked in dequeue opeation.

IsEmpty()
This algorithm check whether a queue is empty or not.
Begin
if (front == -1) then
print : “Underflow : Queu is empty”
else
print: “Queue is not empty”
End
Testing a Linear Queue for Overflow
 Before we insert new element in a queue, it is necessary to test whether queue
still have some space to accomodate the incoming element.
 This condition will be chekced in enqueue operation.

IsFull()
This algorithm check whether a queue is full or not.
Begin
if ( (front == 0) and (rear == CAPACITY – 1))then
Print : “Queue is Full : Overflow”
else
Print: “Queue is not Full”
End
Enqueue Operation on Linear Queue
 First we check whether a Queue is Overflow (Full).

 If the Queue is not full then two conditions occur:


 If a linear queue is empty : fornt = -1 and rear = -1
in this case both front and rear set to 0 and Queue[rear] = value

 If the Linear Queue is not empty, then there are further two possibilities:
 If the value of the rear variable is less than CAPACITY – 1, then the
rear variable is incremented.
 If the value of the rear variable is equal to CAPACITY – 1, then the
elemenets of the linear queue are moved forward, and the front
and rear variables are adusted accordingly.
Enqueue()
Begin
if ((front == 0) and (rear == CAPACITY – 1))then
Print : “Queue is Full : Overflow”
else
if (front == -1)
set front = rear = 0
else if ( rear == CAPACITY – 1 )
for i = fornt to rear by 1 do
Queue[ i – front ] = Queue[i]
rear = rear – front + 1
front = 0
endfor
else
rear = rear + 1
endif
endif
Queue[rear] = value
End
Dequeue Operation on a Linear
Queue
 First we check whether a Queue is Underflow.
 If Queue is not in Underflow condition.
 The front element of a linear queue is assigned to a local variable.
 After assigning the front element of a linear queue to a local variable,
the value of the front variable is modified.
 There are two possibilities :
 If there was only one element in a linear queue, then after Dequeue
operation queue will become empty and value of front and rear
variables will be -1.
 Otherwise the value of the front variable is incremented.
dequeue ( )
Begin
if front == -1 then
print : “Underflow Condition”
else
temp = Queue[ front ]
if ( front == rear )
front = rear = -1
else
front = front + 1
endif
endif
End
Peek Operation (Accessing front
element)
 The element in the front of queue is accessed directly through the front
variable.

Peek( )
Begin
Print : Queue [ front ]
End
Circular Queue

 Circular Queue is a linear data structure in which the operations are


performed based on FIFO (First In First Out) principle
 The last position is connected back to the first position to make a circle. It is
also called ‘Ring Buffer’.
Testing a Circular Queue for Underflow
 Before we remove an element from a queue, it is necessary to test whether
the queue is empty or not.
 This condition will be checked in Dequeue operation.

IsEmpty()
This algorithm check whether a queue is empty or not.
Begin
if (front == -1) then
print : “Underflow : Queu is empty”
else
print: “Queue is not empty”
End
Testing a Circular Queue for Overflow
 Before we insert new element in a queue, it is necessary to test whether queue
still have some space to accomodate the incoming element.
 This condition will be chekced in enqueue operation.

IsFull()
This algorithm check whether a queue is full or not.
Begin
if ( (front == 0) and (rear == CAPACITY – 1) || (front == rear + 1)then
Print : “Queue is Full : Overflow”
else
Print: “Queue is not Full”
End
Enqueue Operation on a Circular
Queue
 First we check whether a circular queue for Overflow condition.

 If a circular queue is not full then there are three conditions


 If the queue is empty, then the value of the front and rear will be -1, then both
front and rear are set to 0.
 If the queue is not empty then the value of the rear will be the index of the last
element of the queue, then the rear variable is incremented.
 If the queue is not full and the value of rear variable is equal to CAPACITY – 1
then rear is set to 0.
Enqueue Algorithm
Enqueue( )
Begin
if ( (front == 0) and (rear == CAPACITY – 1) || (front == rear + 1)then
Print : “Queue is Full : Overflow”
else
if ( front == -1 )
front = rear = 0
else if ( rear == CAPACITY – 1 )
rear = 0
else
rear = rear + 1
endif
Queue[rear] = value
endif
End
Dequeue Operation on a Circular
Queue
 First we check whether a Queue for Underflow condition.
 If Queue is not in Underflow condition.
 The front element of a circular queue is assigned to a local variable.
 After assigning the front element of a linear queue to a local variable,
the value of the front variable is modified.
 There are three possibilities :
 If there was only one element in a circular queue, then after
Dequeue operation queue will become empty and value of front
and rear variables will be -1.
 If value of the front variable is equal to CAPACITY – 1, then set front
variable to 0.
 Otherwise the value of the front variable is incremented.
Dequeue Algorithm
Dequeue ( )
Begin
if (front == -1) then
print : “Underflow : Queue is empty”
else
v = Queue [front]
if( front == rear )
front = rear = -1
else if ( front == CAPACITY – 1)
front = 0
else
front = front + 1
endif
endif
End
Implement Queue Using Stack

 While implementing a queue data structure using stacks, we will have to


consider the natural behavior of stack too, which is First in Last Out.

 We require two Stacks to implement a queue.

 To implement a queue, we can follow two approaches:


 By making the enqueue operation costly
 By making the dequeue operation costly
Making the Enqueue operation costly

 In this approach, we make sure that the oldest element added to the
queue stays at the top of the stack, the second oldest below it and so on.

 To achieve this, we will need two stacks. Following steps will be involved
while enqueuing a new element to the queue.

 NOTE: First stack(S1) is the main stack being used to store the data, while
the second stack(S2) is to assist and store data temporarily during various
operations.
1. If the queue is empty(means S1 is empty), directly push the first element
onto the stack S1.

2. If the queue is not empty, move all the elements present in the first
stack(S1) to the second stack(S2), one by one. Then add the new element
to the first stack, then move back all the elements from the second stack
back to the first stack.

3. Doing so will always maintain the right order of the elements in the stack,
with the 1st data element staying always at the top, with 2nd data element
right below it and the new data element will be added to the bottom.

 This makes removing an element from the queue very simple, all we have
to do is call the pop() method for stack S1.
Making the Dequeue operation costly

 In this approach, we insert a new element onto the stack S1 simply by


calling the push() function, but doing so will push our first element towards
the bottom of the stack, as we insert more elements to the stack.

 But we want the first element to be removed first. Hence in


the dequeue operation, we will have to use the second stack S2.

 We will have to follow the following steps for dequeue operation:


1. If the queue is empty(means S1 is empty), then we return an error message
saying, the queue is empty.

2. If the queue is not empty, move all the elements present in the first
stack(S1) to the second stack(S2), one by one. Then remove the element at
the top from the second stack, and then move back all the elements from
the second stack to the first stack.

3. The purpose of moving all the elements present in the first stack to the
second stack is to reverse the order of the elements, because of which the
first element inserted into the queue, is positioned at the top of the second
stack, and all we have to do is call the pop() function on the second stack
to remove the element.
Priority Queue

 Priority Queue is more specialized data structure than Queue.


 Like ordinary queue, priority queue has same method but with a major
difference.
 In Priority queue items are ordered by key value so that item with the lowest
value of key is at front and item with the highest value of key is at rear or
vice versa.
 So we're assigned priority to item based on its key value.
 Lower the value, higher the priority.
Basic Operations

 enqueue − add an item to the rear of the queue.

 dequeue − remove an item from the front of the queue.

 Peek − get the element at front of the queue.

 isFull − check if queue is full.

 isEmpty − check if queue is empty.


Deque
 A deque, also known as a double-ended queue, is an ordered collection of
items similar to the queue.
 In this the elements can be inserted or deleted at either end in constant
time.
 It is also known as a head-tail linked list because elements can be added
to or removed from either the front (head) or the rear (tail) end.
 However, no element can be added and deleted from the middle.
 There are two variants of a double-ended queue. They include:

 Input restricted deque: In this dequeue, insertions can be done only at one
of the ends, while deletions can be done from both ends.
 Output restricted deque: In this dequeue, deletions can be done only at
one of the ends, while insertions can be done on both ends.
Round-Robin Scheduling Algorithm

 Round robin is a CPU scheduling algorithm that is designed especially for


time sharing systems.
 The name of this algorithm comes from the round-robin principle, where
each person gets an equal share of something in turns.
 It is the oldest, simplest scheduling algorithm, which is mostly used for
multitasking.
 In Round Robin processes are bounded with a quantum time size. A small
unit of time is known as Time Quantum or Time Slice.
Example of Round-robin Scheduling
 Consider this following three processes
 Step 1) The execution begins with process P1, which has burst time 4. Here,
every process executes for 2 seconds. P2 and P3 are still in the waiting
queue.
 Step 2) At time =2, P1 is added to the end of the Queue and P2 starts
executing
 Step 3) At time=4 , P2 is preempted and add at the end of the queue. P3
starts executing.
 Step 4) At time=6 , P3 is preempted and add at the end of the queue. P1
starts executing.
 Step 5) At time=8 , P1 has a burst time of 4. It has completed execution. P2
starts execution
 Step 6) P2 has a burst time of 3. It has already executed for 2 interval. At
time=9, P2 completes execution. Then, P3 starts execution till it completes.
Linked Lists

 A linked list is a linear data structure.


 Unlike arrays, linked list elements are not stored at a contiguous location;
the elements are linked using pointers.
 In simple words, a linked list consists of nodes where each node contains a
data field and a reference(link) to the next node in the list.
Linked List v/s Array

 Arrays can be used to store linear data of similar types, but arrays have the
following limitations.
1) The size of the arrays is fixed: So we must know the upper limit on the
number of elements in advance. Also, generally, the allocated memory is
equal to the upper limit irrespective of the usage.

2) Inserting a new element in an array of elements is expensive because the


room has to be created for the new elements and to create room existing
elements have to be shifted.

3) Deletion is also expensive with arrays until unless some special


techniques are used.
Types of Linked List

 A linked list is a linear collection of elements , called nodes. The linear order
is given by the pointers. Each node is divided into two or more parts.

 A linked list can be of following types:

 Linear Linked list or one-way list or singly Linked List.


 Doubly Linked List or Two-way List
 Circular Linked List
 Header Linked List
Linear Linked List
 In a linear linked list, also called singly linked list of one-way linked list, each
node is divided in two parts:

Information Part
Link field or nextpointer field

 First part contains the information of the element


 Second part contains the address of the next node in the list
 In addition, another pointer variable, head is used to contain the address of
the first element of the list.
 The last element of the linked list have NULL value in the nextpointer field to
mark the end of the list.
head

100

5 201 7 150 10 NULL

100 201 150


Representation of Linear Linked List

 Suppose we want to store list of integer numbers, then the linear linked list
can be represented in memory with the following declarations.

typedef struct nodeType


{
int info;
struct nodeType *next;
}node;

node *head;
Operations on Linear Linked List
Creating an Empty List
 In the data above declaration, the variable head is declared as pointer to
a node type.
 This variable is used to point to the first element of the list.
 Since the list will be empty in the beginning, the variable head is assigned a
NULL value to indicate that list is empty.

createEmptyList( )
Begin
head = NULL
End
Traversing a List

 To traverse the linked list, we move along the pointer, and process each
element till we reach the last element.
Traversal( )
Begin
temp = head
while ( temp != NULL )
print : temp -> info
temp = temp -> next
endwhile
End
Searching an element

 We traverse the list from the beginning, and compare each element of the
list with the given element to be searched.

 If the match occurs, print a message that the element found.

 If we reach the end of the list, and no match found, then print a that the
element not found.
SearchList ( item )
Begin
temp = head
while ( temp != NULL )
if ( temp -> info == item )
print : “Element Found”
break
endif
temp = temp -> next
endwhile
if ( temp == NULL )
print: “Element is not found”
endif
End
Inserting an Element

 Insertion is a process to add an element in a linked list.


 Following are the steps to insert an element in a list:
 Create a free node
 Assign the element to be inserted to the info part of the node
 Then the new node is placed at the appropriate position by adjusting the
appropriate pointers.
 The insertion in the list can take place at the following position :
 At the beginning of the list
 At the end of the list
 After a Giv en Element
Inserting at the beginning of the list

 To insert an element at the beginning of the list, first we test whether the
linked list is initially empty. If yes then the element inserted as the first and
only element of the list by performing the following steps:
 Assign NULL value to the nest pointer field of the new node
 Assign address of the new node to head

head 100
NULL

5 NULL

Ptr =>100
 However, if the list is initially not empty, then the element is inserted as the first element
of the list by performing the following steps:
 Assign v alue of the head v ariable to the next pointer field of the new node.
 Assign the address of the new node to head

head 700
100

5 201 7 150 10 NULL

100 201 150


18 100

ptr = > 700


InsertAtBeginning ( item )
Begin
ptr = ( node * ) malloc ( sizeof ( node ) )
ptr ->info = item
if ( head == NULL )
ptr -> next = NULL
else
ptr->next = head
endif
head = ptr
End
Insertion at the end of List

 To insert the element at the end of the list, first we test whether the linked list
is initially empty. If yes, then the element is inserted as the first and only
element of the list by performing following steps:
 Assign NULL value to the nest pointer field of the new node.
 Assign address of the new node to head.

head 100
NULL

5 NULL

Ptr =>100
 However, if the list then is initially not empty, then the list is traversed to
reach the lat element, and the element is inserted as the last element of
the list by performing the following steps:
 Assign NULL value to the next pointer field of the new node.
 Assign address of the new node to next pointer field of the last node.
head

100

loc

5 201 7 150 10 700


NULL

100 201 150

18 100

ptr = > 700


InsertAtEnd ( item )
Begin
ptr = ( node * ) malloc ( sizeof ( node ) )
ptr->info = item
ptr->next = NULL

if ( head == NULL )
head = ptr
else
loc = head
while ( loc->next != NULL )
loc = loc->next
endwhile
loc->next = ptr
endif
End
Insertion after a Given element

 To insert the new element after the given element, first we check linked is
empty of not, if yes then print a message that linked list is empty.
 If the linked list is not empty then we find the location of, say loc, of the
given element in the list, and check following conditions:
 If the after element is not exist in linked list then print a message that “after
element not found in linked list”.
 If the after element found then insert the new element by performing following
steps:
 Assign the next pointer field of the node pointed by loc to the next pointer field of the
new node.
 Assign address of the new node to the next pointer field of the node pointed by loc.
head

100

loc

5 201 7 700
150 10 NULL

100 201 150

18 150

ptr = > 700


Insertafterelement ( after, item )
Begin
if ( head == NULL )
print: “Linked List is Empty”
else
loc = head
while (loc != NULL )
if (loc->info != after )
loc = loc->next
else
break
endif
endwhile
if ( loc == NULL )
print : “After element Not Found”
else
ptr = (node *) malloc (sizeof(node))
ptr->info – item
ptr->next = loc->next
loc->next = ptr
endif
endif
End
Deleting an Element from List

 To delete an element from the list, first the pointers are set properly and
then the memory occupied by the node to be deleted is deallocated
(freed).

 The deletion in the list can take place at the following positions:
 At the beginning of the list
 At the end of the list
 After a given element
Deleting from the Beginning of the list
 First case is to check Linear Linked list is empty or not.
 If the list is empty then print a message “List is empty”
 If list is not empty then an element from the beginning of the list can be
deleted by performing following steps:
 Assign the value of head to a temporary variable.
head  Assign the value of the next pointer field of the node to head.
 Deallocate the memory occupied by the node pointed to by ptr.
100
201

5 201 7 150 10 NULL

ptr 100 201 150


DeleteFromBegin( )
Begin
if (head == NULL)
Print : “Linked List is Empty”
else
ptr = head
head = ptr->next
free(ptr)
endif
End
Deleting from the end of the list

 First case is to check Linear Linked list is empty or not.


 If the list is empty then print a message “List is empty”
 If list is not empty then check the linked list has only single node. After
deleting this node the linked will be empty.

head NULL
100

5 NULL

Ptr =>100
 If the linked list have more then one node then to delete an element from
the list, we first traverse to the second last element of the list. Then the last
element can be deleted by performing following steps:
 Assign the next pointer filed of the second last node to a temporary variable (say
ptr).
 Assign value NULL to the next pointer field of the second node of the list.
 Deallocate the memory occupied by the node pointed to by ptr.

head

100
150

loc ptr

5 201 7 NULL
150 10 NULL

100 201 150


DeletedFromEnd()
Begin
if (head == NULL)
print : “Linked List is Empty”
else if (head->next == NULL)
ptr = head
head = NULL
free(ptr)
else
loc = head
ptr = loc->next
while(ptr->next != NULL)
loc = ptr
ptr = ptr->next
endwhile
loc->next = NULL
free(ptr)
endif
End
Deleting after a Given element

 First case is to check Linear Linked list is empty or not.


 If the list is empty then print a message “List is empty”
 If the list is not empty then to delete element after a given element, first we
find the location (say loc) of the element after which the element to be
deleted comes.
 If the after element not found then print “After element not found”
 If after element found then perform following steps:
 Assign next pointer field of the node pointed by loc to temporary v ariable (say ptr)
 Assign the next pointer field of the node to be deleted to the node pointed to by loc.
 Deallocate the memory occupied by the node pointed to by ptr.
head

100
150

loc ptr

5 201 7 350
150 10 350 19 NULL

100 201 150 350


DeleteAfterGivenElement( )
Begin
if(head == NUL)
print : “Linked List is Empty”
else if
loc = head
while (loc != NULL )
if (loc->info != after )
loc = loc->next
else
break
endif
endwhile
if ( loc == NULL )
print : “After element Not Found”
else
ptr = loc->next
loc->next = ptr->next
free(ptr)
endif
endif
End
Doubly Linked List
 In a doubly linked list, also called two-way list, each node is divided into
three parts :

Next pointer field


Previouse pointer field
Information Part
 The first part, called previous pointer field, contains the address of the
preceding element in the list.
 The second part contains the information of the element.
 The third part, called next pointer filed, contains the address of the
succeeding element in the list.
 In addition, two pointer variables, head and tail, are used that contains the
address of first element and the address of the last element of the list,
respectively.
tail
head

150
100

NULL 5 201 150 201 10 NULL


100 7

100 201 150


Representation of Doubly Linked List

 Suppose we want to store list of integer numbers, then the doubly linked list
can be represented in memory with the following declarations.

typedef struct nodeType


{
struct nodeType *prev;
int info;
struct nodeType *next;
}node;

node *head, *tail;


Operations on Doubly Linked List
Creating an Empty List
 In the data above declaration, the variable head and tail are declared as
pointer to a node type.
 This head variable is used to point to the first element of the list & tail
variable is used to point to the last element of the list
 Since the list will be empty in the beginning, the variable head & tail are
assigned a NULL value to indicate that list is empty.

createEmptyList( )
Begin
head = tail = NULL
End
Traversing a List

 The doubly linked list can be traversed in two ways:

 In-order Traversal

 Reverse Order Traversal


In-order Traversal

traverseInorder( )
Begin
temp = head
while ( temp != NULL )
print: temp->info
temp = temp->next
endwhile
End
Reverse order Traversal

traverseInReverseorder( )
Begin
temp = tail
while ( temp != NULL )
print: temp->info
temp = temp->prev
endwhile
End
Searching an Element
 The doubly linked list can be traversed in any order to search the given
element.
Search(item)
Begin
temp = head
while ( temp!= NULL )
if(temp->info == item)
print : Item Found
Exit
endif
temp=temp->next
endwhile
if(temp == NULL)
print:Item Not Found
endif
Inserting an Element

 To insert an element in the list, the first task is to get a free node, assign the
element inserted to the info field of the node, and then new node is placed
at the appropriate position by adjusting the appropriate pointer.

 The insertion in the list can take place at the following positions:
 At the beginning of the list
 At the end of the list
 After a given element
 Before a given element
Inserting at the Beginning of the list
 To insert an element at the beginning of the list, first we test whether the
linked list is initially empty.
 If yes, then the element is inserted as the first and only element of the list by
performing the following steps:
 Assign NULL value to the next pointer and previous pointer fields of the new
node.
 Assign address of the new node to head and tail pointer variables.

head 100
NULL tail
NULL
100

NULL 5 NULL

Ptr =>100
 However, if the list is initially not empty, then the element is inserted as the
first element of the list by performing the following steps:
 Assign NULL value to the previous pointer field of the new node.
 Assign value of the head variable to the next pointer field of the new node.
 Assign address of the new node to the previous pointer field of the node
currently pointed by head variable.
tail
 Finally, assign the address of the new to node to head variable.
head

150
100
700

NULL 5 201 100 7 150 201 10 NULL

100 201 150


NULL 2 100

Ptr => 700


InsertAtBegin ( item )
Begin
ptr = (node *) malloc (sizeof(node))
ptr->info = item
if(head == NULL)
ptr->next = ptr->prev = NULL
head = tail =ptr
else
ptr->prev = NULL
ptr->next = head
head->prev = ptr
head = ptr
endif
End
Inserting at the end of the list
 To insert the element at the end of the list, first we test whether the linked list
is initially empty. If yes, then the element is inserted as the first and only
element of the list by performing the following steps:
 Assign NULL value to the next pointer and previous pointer fields of the new
node.
 Assign address of the new node to head and tail pointer variables.

head 100
NULL tail
NULL
100

NULL 5 NULL

Ptr =>100
 However, if the list is initially not empty, then the element is inserted as the
last element of the list by performing following steps:
 Assign NULL value to the next pointer field of the new node.
 Assign value of the tail variable to the previous pointer field of the new node.
 Assign address of the new node to next pointer field of the node currently tail
pointed by tail.
head
 Finally, assign the address of the new to node to tail steps.

150
100
700

NULL 5 201 100 7 150 201 10 700


NULL

100 201 150

150 2 NULL

Ptr => 700


InsertAtEnd(item)
Begin
ptr = (node *) malloc(sizeof(node))
ptr->info = item

if(head == NULL)
ptr->next = ptr->prev=NULL
head=tail=ptr
else
ptr->prev = tail
ptr->next = NULL
tail->next= ptr
tail = ptr
endif
End
Insertion after a Given element

 To insert the new element after the given element, first we check linked is
empty of not, if yes then print a message that linked list is empty.
 If the linked list is not empty then we find the location of, say loc, of the
given element in the list, and check following conditions:
 If the after element is not exist in linked list then print a message that “after
element not found in linked list”.
 If the after element found then insert the new element according to following
case:
 If the loc is at the end of the list
 If the loc is at before the end of the list
InsertAfterElement(after, item)
Begin
if ( head == NULL )
print: “Linked List is Empty”
else
loc = head
while (loc != NULL )
if (loc->info != after )
loc = loc->next
else
break
endif
endwhile
if ( loc == NULL )
print : “After element Not Found”
else if (loc->next == NULL)
ptr->next = NULL
loc->next = ptr
ptr->prev=tail
tail = ptr
else
ptr->prev = loc
ptr->next = loc->next
(loc->next)->prev = ptr
loc->next = ptr
endif
End
Deleting an Element

 To delete an element from the list, first the pointers are set properly and
then the memory occupied by the node to be deleted is allocated (freed).

 The deletion in the list can take place at the following positions:
 At the beginning of the list
 At the end of the list
 After a given element
 Before a given element
Deleting from the beginning of the list

 An element from the beginning of the lists can be deleted by performing


following steps:
 If the list is empty then print a message
 If the list is not empty then assign the value of head to a temporary variable.
 Further, there are two cases:
 If there is only one element in the existing list, both head and tail v ariable set to NULL.
 If there are more than one element in the list, then following steps giv en below:
 Assign NULL value to the previous pointer field of the second node.
 Assign address of the second node to head.

 Deallocate the memory occupied by the node pointed to by ptr.


DeleteFromBegin()
Begin
if (head == NULL)
print: “Linked List is Empty”
else
ptr = head
if (head == tail)
head = tail = NULL
else
(ptr->next)->prev = NULL
head = ptr->next
endif
free(ptr)
endif
End
Deleting from the End of the list

 An element from the end of the lists can be deleted by performing


following steps:
 If the list is empty then print a message
 If the list is not empty then assign the value of tail to a temporary variable ptr
 Further, there are two cases:
 If there is only one element in the existing list, both head and tail v ariable set to NULL.
 If there are more than one element in the list, then following steps giv en below:
 Assign NULL value to the next pointer field of the second last node.
 Assign address of the second last node to tail.

 Deallocate the memory occupied by the node pointed to by ptr.


DeleteFromEnd()
Begin
if (head == NULL )
print: “Linked List is Empty”
else
ptr = tail
if( head == tail )
head = tail = NULL
else
(ptr->prev)->next = NULL
tail = ptr->prev
endif
free (ptr)
endif
End
Delete After a Given Element

 First case is to check Doubly Linked list is empty or not.


 If the list is empty then print a message “List is empty”
 If the list is not empty then to delete element after a given element, first we
find the location (say loc) of the element after which the element to be
deleted comes.
 If the after element not found then print “After element not found”
 If after element found then further there are two cases:
 If the loc is at just before the end of the list
 If the loc is at in between the list
DeleteAfterElement(after)
Begin
if ( head == NULL )
print: “Linked List is Empty”
else
loc = head
while (loc != NULL )
if (loc->info != after )
loc = loc->next
else
break
endif
endwhile
if ( loc == NULL )
print : “After element Not Found”
else if ((loc->next)->next == NULL)
ptr = loc->next
loc->next = NULL
tail = loc
free(ptr)
else
ptr = loc->next
loc->next = ptr->next
(ptr->next)->prev = loc
free(ptr)
endif
endif
End
Stack implementation Using a Linked
List
 A stack represented using a linked list is also known as linked stack.

 The array based representation of stacks suffers from following limitations:

 Size of the stack must be known in advance.


 We may come across situations when an attempt to push an element causes
overflow. However, stack, as an abstract data structure can not be full. Hence,
abstractly, it is always possible to push an element onto stack. Therefore,
representing stack as an array prohibits the growth of stack beyond the finite
number of elements.
Stack Linked List Representation

typedef struct nodeType


{
int info;
struct nodeType *next;
}stack;

stack *top;
top

100

5 201 7 150 10 NULL

100 201 150


top

700

6 100 5 201 7 150 10 NULL

700 100 201 150


Creating an Empty Stack

 Before we can use a stack, it is to be initialized.


 To initialize a stack, we will create an empty linked list.
 The empty stack is created by setting pointer variable top to value NULL.

createstack( )
Begin
top = NULL
End
Testing Stack for Underflow
 The stack is tested for underflow condition by checking whether the linked
list is empty.
 The empty status of the stack will be indicated by the NULL value of pointer
variable top.
Underflow( )
Begin
if( top == NULL )
print : “Underflow”
else
print : “Not Empty”
endif
End
Testing Stack for Overflow

 Since a stack represented using linked list can grow to a limit of a computer
memory, there overflow condition never occurs.

 Hence this operation is not implemented for linked stacks.


Push Operation
 To push a new element onto stack, the element is inserted in the beginning
of the linked list.
Push( item )
Begin
ptr = (stack *) malloc(sizeof(stack))
ptr->info = item
if(top == NULL)
ptr->next = NULL
else
ptr->next = top
endif
top = ptr
End
Pop Operation
 To pop an element from the stack, the element is removed from the
beginning of the linked list.

Pop( )
Begin
if(top == NULL)
print : “Stack is Empty : Underflow”
else
ptr = top
top = ptr->next
free(ptr)
endif
End
Queue Implementation using Linked
List
 The major problem with the queue implemented using an array is, It will
work for an only fixed number of data values.
 That means, the amount of data must be specified at the beginning itself.
Queue using an array is not suitable when we don't know the size of data
which we are going to use.
 A queue data structure can be implemented using a linked list data
structure.
 The queue which is implemented using a linked list can work for an
unlimited number of values.
Memory Representation of Queue

typedef struct nodeType


{
int info;
struct nodeType *next;
}queue;

queue *front, *rear;


Create an Empty Queue

 In the data above declaration, the variable front and rear are declared as
pointer to a node type.
 This front variable is used to point to the first element of the linked queue &
rear variable is used to point to the last element of the linked queue.
 Since the linked queue will be empty in the beginning, the variable front &
trear are assigned a NULL value to indicate that list is empty.

createEmptyQueue( )
Begin
front= rear = NULL
End
Testing a Linear Queue for Underflow
 Before we remove an an element from a queue, it is necessary to test
whether the queue is empty or not.
 This condition will be checked in dequeue opeation.

IsEmpty()
This algorithm check whether a queue is empty or not.
Begin
if (front == NULL) then
print : “Underflow : Queue is empty”
else
print: “Queue is not empty”
End
Testing a Linear Queue for Underflow

 No overflow condition occur, when we implement linear queue using linked


list.

 Because linked list is a dynamic data structure & node created at the run
time.

 So complete computer memory is available for node.


Enqueue Operation on Linear Queue
 The enqueue operation append the queue by adding an element to the
end of the queue. The new element will be the last element of the queue.
 This operation is same as insertion at the end of the linked list. When a new
element comes for insertion it will inserted at the rear end of the queue or
we can say at the end of the linked list.
 First condition is to check whether a linked queue is empty or not.

 If yes, then the element is inserted as the first and only element of the
queue by performing following steps:
 Assign NULL value to the next pointer field of the new node.
 Assign address of the new node to front & rear.
 However, if the queue is initially not empty, then the element is inserted as
the last element of the list by performing the following steps:

 Assign NULL value to the next pointer field of the new node.
 Assign address of the new node to next pointer field of the last node.
 Assign the address of new node to rear.
Enqueue ( item )
Begin
ptr = ( node * ) malloc ( sizeof ( node ) )
ptr->info = item
ptr->next = NULL

if ( front == NULL )
front = rear = ptr
else
rear->next = ptr
rear = ptr
endif
End
Dequeue Operation on Linear Queue

 The dequeue operation remove the queue element from the beginning of
the queue.
 This operation is same as deletion from the beginning of the linked list.
When we want to delete an element from the queue it will delete the front
element rom the queue.
 First condition is to check whether a linked queue is empty or not.
 If the queue is empty then print a message : “Queue is empty : Underflow”
 If the queue is not empty then there are two case:
 If only single element exist is the queue.
 More then one element av ailable in queue.
Dequeue( )
Begin
if (front == NULL)
Print : “Queue is Empty : Underflow”
else if (front == rear)
ptr = front
front = rear = NULL
free(ptr)
else
ptr = front
front = ptr->next
free(ptr)
endif
End
Circular Linked List
 In single linked list, every node points to its next node in the sequence and
the last node points NULL.
 But in circular linked list, every node points to its next node in the sequence
but the last node points to the first node in the list.
 A circular linked list is a sequence of elements in which every element has
a link to its next element in the sequence and the last element has a link to
the first element.
 That means circular linked list is similar to the single linked list except that the
last node points to the first node in the list
Representation of Circular Linked List

 Suppose we want to store list of integer numbers, then the circular linked list
can be represented in memory with the following declarations.

typedef struct nodeType


{
int info;
struct nodeType *next;
}node;

node *head;
Operations on Circular Linked List
Creating an Empty List
 In the data above declaration, the variable head is declared as pointer to
a node type.
 This variable is used to point to the first element of the list.
 Since the list will be empty in the beginning, the variable head is assigned a
NULL value to indicate that list is empty.

createEmptyList( )
Begin
head = NULL
End
Traversing a List

 To traverse the linked list, we move along the pointer, and process each
element till we reach the last element.
Traversal( )
Begin
temp = head
do
print : temp -> info
temp = temp -> next
while ( temp!= head )
End
Searching an element

 We traverse the list from the beginning, and compare each element of the
list with the given element to be searched.

 If the match occurs, print a message that the element found.

 If we reach the end of the list, and no match found, then print a that the
element not found.
SearchList ( item )
Begin
temp = head
do
if ( temp -> info == item )
print : “Element Found”
exit
endif
temp = temp -> next
while ( temp != head )
if ( temp == head )
print: “Element is not found”
endif
End
Inserting an Element

 Insertion is a process to add an element in a linked list.


 Following are the steps to insert an element in a list:
 Create a free node
 Assign the element to be inserted to the info part of the node
 Then the new node is placed at the appropriate position by adjusting the
appropriate pointers.
 The insertion in the list can take place at the following position :
 At the beginning of the list
 At the end of the list
 After a Giv en Element
Inserting at the beginning of the list

 To insert an element at the beginning of the list, first we test whether the
linked list is initially empty. If yes then the element inserted as the first and
only element of the list by performing the following steps:
 Assign head value to the next pointer field of the new node
 Assign address of the new node to head

head 100
NULL

5 100

Ptr =>100
 However, if the list is initially not empty, then the element is inserted as the first element
of the list by performing the following steps:

 Define pointer ‘temp' and initialize with 'head'.


 Keep mov ing the 'temp' to its next node until it reaches to the last node
 Assign v alue of the head v ariable to the next pointer field of the new node.
 Assign the address of the new node to head
 Assign head v alue to the next pointer field of temp.
InsertAtBeginning ( item )
Begin
ptr = ( node * ) malloc ( sizeof ( node ) )
ptr ->info = item
if ( head == NULL )
head = ptr
ptr -> next = head
else
temp = head
do
temp = temp -> next
while ( temp->next!= head )
ptr->next = head
head = ptr
temp->next = head
endif
End
Insertion at the end of List

 To insert the element at the end of the list, first we test whether the linked list
is initially empty. If yes, then the element is inserted as the first and only
element of the list by performing following steps:
 Assign head value to the nest pointer field of the new node.
 Assign address of the new node to head.

head 100
NULL

5 100

Ptr =>100
 However, if the list then is initially not empty, then the list is traversed to
reach the lat element, and the element is inserted as the last element of
the list by performing the following steps:

 Define a node pointer temp and initialize with head.


 Keep moving the temp to its next node until it reaches to the last node in the list.
 Assign head value to the next pointer field of the new node.
 Assign address of the new node to next pointer field of the temp.
InsertAtEnd ( item )
Begin
ptr = ( node * ) malloc ( sizeof ( node ) )
ptr->info = item
if ( head == NULL )
head = ptr
ptr -> next = head
else
temp = head
do
temp = temp -> next
while ( temp->next!= head )
ptr->next = head
temp->next = ptr
endif
End
Deleting an Element from List

 To delete an element from the list, first the pointers are set properly and
then the memory occupied by the node to be deleted is deallocated
(freed).

 The deletion in the list can take place at the following positions:
 At the beginning of the list
 At the end of the list
 After a given element
Deleting from the Beginning of the list
 First case is to check Circular Linked list is empty or not.

 If the list is empty then print a message “List is empty”

 If list is not empty then an element from the beginning of the list can be
deleted by performing following steps:

 If there is only one element in the existing list, head v ariable set to NULL.
 If there are more than one element in the list, then following steps giv en below:
 Define a node pointers ptr & temp and initialize with head.

 Keep moving the temp to its next node until it reaches to the last node in the list .

 Then set head to ptr->next & temp->next to head


DeleteFromBegin( )
Begin
if (head == NULL)
Print : “Linked List is Empty”
else
ptr = head
if (head == ptr->next)
head = NULL
free(ptr)
else
temp = head
do
temp = temp -> next
while ( temp->next!= head )
head = ptr->next
temp->next = head
free(ptr)
endif
End
Deleting from the end of the list

 First case is to check Linear Linked list is empty or not.


 If the list is empty then print a message “List is empty”
 If list is not empty then an element from the beginning of the list can be
deleted by performing following steps:

 If there is only one element in the existing list, head v ariable set to NULL.
 If there are more than one element in the list, then following steps giv en below:
 Define a node pointers ptr initialize with head & temp and initialize with NULL.

 Keep moving the ptr & temp to its next node until it ptr reaches to the last node in the list &
temp reaches to the second last node in the list. .
 Then set temp->next to head & free ptr
DeleteFromEnd( )
Begin
if (head == NULL)
Print : “Linked List is Empty”
else
ptr = head
if (head == ptr->next)
head = NULL
free(ptr)
else
ptr = head
temp = NULL
do
temp = ptr
ptr = ptr -> next
while ( ptr->next!= head )
temp->next = head
free(ptr)
endif
End
Header Linked List

 A Header linked list is one more variant of linked list.


 In Header linked list, we have a special node header node present at the
beginning of the linked list.
 A list that contains this type of node, is called the header-linked list.
 This type of list is useful when information other than that found in each
node is needed.
 We can use this node to store number of nodes present in the linked list.
Types of Header Linked List
 Grounded Header Linked List

 It is a list whose last node contains the NULL pointer.


 In the header linked list the start pointer always points to the header node.
 start -> next = NULL indicates that the grounded header linked list is empty.
 The operations that are possible on this type of linked list are Insertion, Deletion,
and Traversing.
 Circular Header Linked List
 A list in which last node points back to the header node is called circular linked
list.
 The chains do not indicate first or last nodes.
 In this case, external pointers provide a frame of reference because last node of
a circular linked list does not contain the NULL pointer.
 The possible operations on this type of linked list are Insertion, Deletion and
Traversing.
Searching Techniques

 Searching is the process of finding the location of given element in the


linear array.
 The search is said to be successful if the given element is found i.e., the
element does exists in the array; otherwise unsuccessful.
 There are two approaches to search operation:

 Linear or Sequential Search


 Binary Search
Linear or Sequential Search

 Linear search is a very simple search algorithm.


 In this type of search, a sequential search is made over all items one by
one.
 Every item is checked and if a match is found then that particular item is
returned, otherwise the search continues till the end of the data collection.
 If the elements are in random order, then one have to use linear search
technique to search an element.
Lets search for the number 3. We start at the beginning and check the first element in the
array. Is it 3?

No, not it. Is it the next element?

Not there either. The next element?

Not there either. Next?

We found it!!!
LinearSearch(a, n, item, loc)
Begin
for i = 0 to ( n – 1 ) by 1 do
if (a[ i ] == item ) then
loc = i
exit
endif
endfor

loc = -1
End
Binary Search

 Binary search is a fast search algorithm.


 This search algorithm works on the principle of divide and conquer. For this
algorithm to work properly, the data collection should be in the sorted form.
 Binary search looks for a particular item by comparing the middle most item
of the collection.
 If a match occurs, then the index of item is returned.
 If the middle item is greater than the item, then the item is searched in the
sub-array to the left of the middle item.
 Otherwise, the item is searched for in the sub-array to the right of the
middle item.
 This process continues on the sub-array as well until the size of the subarray
reduces to zero.
 Let us consider an array
arr = {1, 5, 7, 8, 13, 19, 20, 23, 29}.
 Find the location of the item 23 in the array.
 In 1st step :
beg = 0
end = 8
mid = 4
a[mid] = a[4] = 13 < 23, therefore
 in Second step:
beg = mid +1 = 5
end = 8
mid = 13/2 = 6
a[mid] = a[6] = 20 < 23, therefore;
 in third step:
beg = mid + 1 = 7
end = 8
mid = 15/2 = 7
a[mid] = a[7]
a[7] = 23 = item;
therefore, set location = mid;
 The location of the item will be 7.
BinarySearch(a, n, item, loc)
Begin
beg = 0
end = n – 1
mid = ( beg + end ) / 2

while ( (beg <= end) and (a[mid] != item) ) do


if( item < a[mid]) then
end = mid – 1
else
beg = mid + 1
endif
mid = (beg + end ) / 2
endwhile
if ( beg > end ) then
loc = -1
else
loc = mid
endif
End
Sorting

 Sorting is the process of arranging the elements in some logical order.

 The logical order may be ascending or descending in case of numeric


values or dictionary order in case of alphanumeric values.

 Sorting is classified into following categories:


 Internal Sorting – deals with sorting the data held in memory of the computer.
 External Sorting – deals with sorting the data stored in data files. This method is
used when the volume of the data is very large and cannot be held in
computer’s main memory.
Bubble Sort
 Bubble Sort is a simple algorithm which is used to sort a given set
of n elements provided in form of an array with n number of elements.
 Bubble Sort compares all the element one by one and sort them based on
their values.
 If the given array has to be sorted in ascending order, then bubble sort will
start by comparing the first element of the array with the second element, if
the first element is greater than the second element, it will swap both the
elements, and then move on to compare the second and the third
element, and so on.
 If we have total n elements, then we need to repeat this process for n-
1 times.
 It is known as bubble sort, because with every complete iteration the
largest element in the given array, bubbles up towards the last place or the
highest index, just like a water bubble rises up to the water surface.
 Let's consider an array with values {5, 1, 6, 2, 4, 3}
BubbleSort( a, n )
Begin
for i = 0 to ( n – 1 ) by 1 do
for j = 0 to ( n – i ) by 1 do
if ( a[ j ] > a[ j + 1 ] ) then
temp = a[ j ]
a[ j ] = a[ j + 1 ]
a[ j + 1 ] = temp
endif
endfor
endfor
End
Selection Sort

 Selection sort is a simple sorting algorithm.


 This sorting algorithm is an in-place comparison-based algorithm in which
the list is divided into two parts, the sorted part at the left end and the
unsorted part at the right end.
 Initially, the sorted part is empty and the unsorted part is the entire list.
 The smallest element is selected from the unsorted array and swapped with
the leftmost element, and that element becomes a part of the sorted
array.
 This process continues moving unsorted array boundary by one element to
the right.
For the first position in the sorted list, the whole list is scanned sequentially. The first position
where 14 is stored presently, we search the whole list and find that 10 is the lowest value.

So we replace 14 with 10. After one iteration 10, which happens to be the minimum value in
the list, appears in the first position of the sorted list.

For the second position, where 33 is residing, we start scanning the rest of the list in a linear
manner.

We find that 14 is the second lowest value in the list and it should appear at the second place.
We swap these values.

After two iterations, two least values are positioned at the beginning in a sorted manner.
The same process is applied to the rest of the items in the array.
Following is a pictorial depiction of the entire sorting process −
SelectionSort( a, n )
Begin
for i = 1 to ( n – 1 ) by 1 do
small = a[ i ]
loc = i – 1
for j = i to ( n – 1 ) by 1 do
if ( a[ j ] < small ) then
small = a[ j ]
loc = j
endif
endfor
temp = a[ i – 1]
a[ i – 1 ] = a[ loc ]
a[ loc ] = temp
endfor
End
Insertion Sort

 This is an in-place comparison-based sorting algorithm.


 Here, a sub-list is maintained which is always sorted.
 For example, the lower part of an array is maintained to be sorted. An
element which is to be inserted in this sorted sub-list, has to find its
appropriate place and then it has to be inserted there. Hence the
name, insertion sort.
 The array is searched sequentially and unsorted items are moved and
inserted into the sorted sub-list (in the same array).
Insertion sort compares the first two elements.

It finds that both 14 and 33 are already in ascending order. For now, 14 is in sorted sub-list.

Insertion sort moves ahead and compares 33 with 27.

And finds that 33 is not in the correct position.

It swaps 33 with 27. It also checks with all the elements of sorted sub-list. Here we see that
the sorted sub-list has only one element 14, and 27 is greater than 14. Hence, the sorted
sub-list remains sorted after swapping.
By now we have 14 and 27 in the sorted sub-list. Next, it compares 33 with 10.

These values are not in a sorted order.

So we swap them.

However, swapping makes 27 and 10 unsorted.

Hence, we swap them too.

Again we find 14 and 10 in an unsorted order.

We swap them again. By the end of third iteration, we have a sorted sub-list of 4 items.
InsertionSort( a, n )
Begin
for k = 1 to (n – 1) by 1 do
temp = a[k]
j=k–1
while( (temp < a[ j ] ) and ( j >= 0 ) ) do
a[ j + 1 ] = a[ j ]
j=j-1
endwhile

a[ j + 1 ] = temp
End
Radix Sort / Bucket Sort
 Radix sort is a small method that many people intuitively use when
alphabetizing a large list of names.
 Specifically, the list of names is first sorted according to the first letter of
each name, that is, the names are arranged in 26 classes.
 Intuitively, one might want to sort numbers on their most significant digit.
However, Radix sort works counter-intuitively by sorting on the least
significant digits first.
 On the first pass, all the numbers are sorted on the least significant digit and
combined in an array.
 Then on the second pass, the entire numbers are sorted again on the
second least significant digits and combined in an array and so on.
Consider Following list of numbers:
321, 150, 235, 65, 573, 789, 928, 542
RadixSort(a, n)
Begin
Find the largest number of the array
Set digitCount = Number of digits of the largest number in the given array
for pass = 1 to digitCount by 1 do
Initialize buckets
for i = 1 to (n-1) by 1 do
Set digit = Obtain digit number pass of a[i]
Put a[i] in bucket number digit
Increment bucket count for bucket numbered digit
endfor
Collect all the numbers from buckets in order
endfor
End
Merge Sort

 Merge Sort is a kind of Divide and Conquer algorithm in computer


programming.

 It is one of the most popular sorting algorithms and a great way to develop
confidence in building recursive algorithms.

 Merge sort first divides the array into equal halves and then combines them
in a sorted manner.
 Merge Sort procedure is recursive, with base criteria – the number of
elements in the array are not more than 1.
 Suppose variable beg and end represents the index of the first and last
element of the array respectively, the merge sort can be defined
recursively as

MergeSort(a, beg, end)


Begin
if (beg < end) then
mid = (beg+end) / 2
Call MergeSort(a, beg, mid)
Call MergeSort(a, mid+1, end)
Call MergingSortedSubArrays(a, beg, mid, mid+1, end);
End
Merge Two Arrays

 Merging is the process of combining the elements of two linear arrays into a
single structure.
MergingSortedSubArrays(a, lb, lr, rb, rr)
Here a is a linear array, variables lb, lr represents beginning and ending index of the
left sub array, and variables rb, rr represents beginning and ending index of the right
sub array. It uses local variables na, nb as counters to keep track of the elements of
left and right sub array respectively, which are candidates to go to temporary array
c, and nc as counter to keep track of the position in c, where the incoming elements
will be stored.
Begin
Set na = lb, nb = rb, and nc = lb
while ((na <= lr) and (nb < rr)) do
if( a[na] < b[nb] ) then
set c[nc] = a[na]
set na = na + 1
else
set c[nc] = b[nb]
set nb = nb + 1
endif
set nc = nc + 1
endwhile
if ( na > lr ) then
while ( nb <= rr ) then
set c[nc] = b[nb]
set nb = nb + 1
set nc = nc + 1
endwhile
else
while ( na <= lr ) then
set c[nc] = a[na]
set na = na + 1
set nc = nc + 1
endwhile
endif
for k = lb to rr by 1 do
set a[k] = c[k]
endfor
End
Counting Sort

 It is a linear time sorting algorithm which works faster by not making a


comparison.
 Basic idea is to determine the "rank" of each number in the final sorted
array.
 Counting Sort uses three arrays:
 A [1, n] holds initial input.
 B [1, n] holds sorted output.
 C [1, k] is the array of integer. C [x] is the rank of x in A where x ∈ [1, k]
Firstly C [x] to be a number of elements of A [j] that is equal to x
Counting Sort (array P, array Q, int k)
for i ← 1 to k do
C [i] ← 0 [ θ(k) times]
for j ← 1 to length [A] do
C[A[j]] ← C [A [j]]+1
// C [i] now contain the number of elements equal to i
for i ← 2 to k do
C [i] ← C [i] + C[i-1]
//C[i] now contain the number of elements ≤ i
for j ← length [A] down to 1 do
B[C[A[j] ← A [j]
C[A[j] ← C[A[j]-1
UPDATED C is: C now contains a count of elements of A

Now, the for loop i= 2 to 7 will be executed having statement:


C [i] = C [i] + C [i-1]
B now contains the final sorted data.
Quick Sort

 Quick sort is a sorting algorithm that also, like merge sort , uses the idea of
divide and conquer.
 This algorithm finds the element, called pivot, that partitions the array into
two halves in such a way that the elements in the left sub array are less that
and the elements in right sub array are greater than the partitioning
element.
 Then this two sub arrays are sorted separately.
 This procedure is recursive in nature with the base criteria – the number of
elements in the array are not more than 1.
 Suppose variable beg and end represents the index of the first and last
element of the array respectively, the quick sort can be defined recursively
as
if ( beg < end ) then
Find element that partition the array into two halves
Quicksort the left half
Quicksort the right half
end
 The main task in Quick sort is to find the element that partitions the array
into two halves and to place it at its proper location in the array.
 Usually, the procedure places the first element in the array at its final
location.
 This task is performed as stated below:
 To begin with, set the index of the first element of the to loc and left variables,
and index of the last element of the array to right variable. Then proceed as
follows:
1. Beginning with the element pointed to by right, the array is scanned from right
to left, comparing each element on the way with the element pointed to by
loc, till either
 Element smaller than the element pointed to by loc is found. In this case, the elements are
interchanged and procedure continue with step 2.
 If the value of the right variable becomes equal to the v alue of loc, the procedure
terminates here. This condition indicates that the element is placed in its final position loc.

2. Beginning with the element pointed to by left, the array is scanned from left to
right, comparing each element on the way with the element pointed to by loc,
till either
 Element greater than the element pointed to by loc is found. In this case, the elements are
interchanged and procedure continue with step 1.
 If the value of the left variable becomes equal to the value of loc, the procedure terminates
here. This condition indicates that the element is placed in its final position loc.

 As this procedure terminates, the first element, pivot, of original the array will be
placed as loc, its final location in the sorted array. The elements to left of it will
be less than this element and elements to its right will be greater than this
element.
PartitionArray( a, beg, end, loc )
Begin
Set left = beg, right = end, loc = beg
Set done = false

while ( not done ) do


while ( a[loc] <= a[right] ) and ( loc != right ) ) do
Set right = right – 1
endwhile

if ( loc == right ) then


Set done = true
else if ( a[loc] > a[right] ) then
Interchange a[loc] and a[right]
Set loc = right
endif
if ( not done ) then
while ( ( a[loc] >= a[left] ) and ( loc != left ) ) do
Set left = left + 1
endwhile

if ( loc == left ) then


Set done = true
else if ( a[loc] < a[left] ) then
Interchange a[loc] and a[left]
Set loc = left
endif
endif
endwhile
End
 Now the Quick Sort takes the following form

QuickSort( a, lb, ub )
Begin
if ( lb < ub ) then
Call PartitionArray(a, lb, ub, loc)
Call QuickSort(a, lb, loc-1)
Call QuickSort(a, loc+1, ub)
endif
End
Tree

 A Tree is a recursive data structure containing the set of one or more data
nodes where one node is designated as the root of the tree while the
remaining nodes are called as the children of the root.
 The nodes other than the root node are partitioned into the non empty sets
where each one of them is to be called sub-tree.
 Nodes of a tree either maintain a parent-child relationship between them
or they are sister nodes.
 In a general tree, A node can have any number of children nodes but it
can have only a single parent.
 The following image shows a tree, where the node A is the root node of the
tree while the other nodes can be seen as the children of A.
Basic terminology
 Root Node :- The root node is the topmost node in the tree hierarchy. In
other words, the root node is the one which doesn't have any parent.
 Sub Tree :- If the root node is not null, the tree T1, T2 and T3 is called sub-
trees of the root node.
 Leaf Node :- The node of tree, which doesn't have any child node, is called
leaf node. Leaf node is the bottom most node of the tree. There can be
any number of leaf nodes present in a general tree. Leaf nodes can also be
called external nodes.
 Path :- The sequence of consecutive edges is called path. In the tree shown
in the above image, path to the node E is A→ B → E.
 Ancestor node :- An ancestor of a node is any predecessor node on a path
from root to that node. The root node doesn't have any ancestors. In the
tree shown in the previous slide image, the node F have the ancestors, B
and A.
 Degree of a node :- Degree of a node is equal to number of children, a
node have. In the tree shown in the previous slide, the degree of node B is
2. Degree of a leaf node is always 0 while in a complete binary tree,
degree of each node is equal to 2.
 Level Number :- Each node of the tree is assigned a level number in such a
way that each node is present at one level higher than its parent. Root
node of the tree is always present at level 0.
 Height of a node :- The height of a node is the number of edges from the
node to the deepest leaf (i.e. the longest path from the node to a leaf
node).
 Depth of a Node :- The depth of a node is the number of edges from the
root to the node.
 Height / Depth of a Tree :- The height of a Tree is the height of the root node
or the depth of the deepest node.
Binary Tree

 Binary Tree is a special type of generic tree in which, each node can have
at most two children. Binary tree is generally partitioned into three disjoint
subsets.
 Root of the node
 left sub-tree which is also a binary tree.
 Right sub-tree which is also a binary tree.
 A binary Tree is shown in the following image.
Binary Tree Representation

 There are two types of representation of a binary tree:

 Array or Sequential Representation


 Linked List Representation
Array or Sequential Representation
 This is the simplest memory allocation technique to store the tree elements
but it is an inefficient technique since it requires a lot of space to store the
tree elements.
 A binary tree is shown in the following figure along with its memory
allocation.
 In this representation, an array is used to store the tree elements.
 Size of the array will be equal to the number of nodes present in the tree.
 The root node of the tree will be present at the 1st index of the array.
 If a node is stored at ith index then its left and right children will be stored at
2i and 2i+1 location.
 If the 1st index of the array i.e. tree[1] is 0, it means that the tree is empty.
Linked List Representation

 In this representation, the binary tree is stored in the memory, in the form of
a linked list where the number of nodes are stored at non-contiguous
memory locations and linked together by inheriting parent child relationship
like a tree.
 Every node contains three parts : pointer to the left node, data element and
pointer to the right node.
 Each binary tree has a root pointer which points to the root node of the
binary tree. In an empty binary tree, the root pointer will point to null.
 Consider the binary tree given in the figure below.

 In the above figure, a tree is seen as the collection of nodes where each
node contains three parts : left pointer, data element and right pointer.
 Left pointer stores the address of the left child while the right pointer stores
the address of the right child.
 The leaf node contains NULL in its left and right pointers.
 The following image shows about how the memory will be allocated for the
binary tree by using linked representation.
 There is a special pointer maintained in the memory which points to the
root node of the tree.
 Every node in the tree contains the address of its left and right child. Leaf
node contains null in its left and right pointers.
Binary Search Tree

 Binary Search tree can be defined as a class of binary trees, in which the
nodes are arranged in a specific order. This is also called ordered binary
tree.
 In a binary search tree, the value of all the nodes in the left sub-tree is less
than the value of the root.
 Similarly, value of all the nodes in the right sub-tree is greater than or equal
to the value of the root.
 This rule will be recursively applied to all the left and right sub-trees of the
root.
 A Binary search tree is shown in the above figure. As the constraint applied on the
BST, we can see that the root node 30 doesn't contain any value greater than or
equal to 30 in its left sub-tree and it also doesn't contain any value less than 30 in its
right sub-tree.
Tree node Representation
 In Linked list representation, each element is representation by a node that
has exactly two link fields. Let us call these fields left and right.
 In addition to these tow link fields, each node has a data field called info.
 This node structure is defined as
typedef struct nodeType
{
struct nodeType *left;
int info;
struct nodeType *right;
}BST;

BST *root;
Creating an empty BST

 Since the binary search tree will be empty in the beginning.


 The variable root is assigned a sentinel value to indicate the binary search
tree is empty.

createrTree()
Begin
root = NULL
end
Traversing a BST

 Recall, that to traverse a linear linked list, we set a temporary pointer


variable to head of the list, and then follow the links from one node to the
other until we reach a node whose next pointer field contains NULL value.
 Similarly, we can set a pointer variable to the root of the tree.
 But where do we go from there – to the left child or the right child?
 Do we process the root first or the leaves first?
 Therefore, based on the way the nodes in the tree are traversed, the
traversal is classified in one of the following categories for a binary tree T
with root.
 Preorder traversal
 In-order Traversal
 Post-order Traversal
Root

 Pre order traversal


 Process the root R
 Traverse the left subtree of R in Pre-order
 Traverse the right subtree of R in preorder
 In order traversal
Left Right
 Traverse the left subtree of R in In-order Subtree Subtree
 Process the root R
 Traverse the right subtree of R in in-order
 Post order traversal
 Traverse the left subtree of R in post-order
 Traverse the right subtree of R in post-order
 Process the root R
Pre order Traversal-

100 , 20 , 10 , 30 , 200 , 150 , 300

In order Traversal-
10 , 20 , 30 , 100 , 150 , 200 , 300

Pos torder Traversal-

10 , 30 , 20 , 150 , 300 , 200 , 100


Exercise

Find Pre order, In order & Post Order Traversal?


Pre-Order Traversal of BST
 Pre order traversal
 Process the root R
 Traverse the left subtree of R in Pre-order
 Traverse the right subtree of R in preorder
 The recursive implementation of pre-order traversal algorithm :
preorderTraversalRecursive()
{
tree = root
while(tree != NULL)
{
print : tree->info
preorderTraversalRecursive(tree->left)
preorderTraversalRecursive(tree->right)

}
}
In-Order Traversal of BST
 In order traversal
 Traverse the left subtree of R in In-order
 Process the root R
 Traverse the right subtree of R in in-order
 The recursive implementation of In-order traversal algorithm :
inorderTraversalRecursive()
{
tree = root
while(tree != NULL)
{
inorderTraversalRecursive(tree->left)
print : tree->info
inorderTraversalRecursive(tree->right)
}
}
Post-Order Traversal of BST
 Post order traversal
 Traverse the left subtree of R in post-order
 Traverse the right subtree of R in post-order
 Process the root R
 The recursive implementation of Post-order traversal algorithm :
postorderTraversalRecursive()
{
tree = root
while(tree != NULL)
{
postorderTraversalRecursive(tree->left)
postorderTraversalRecursive(tree->right)
print : tree->info
}
}
Searching
 Searching is a process to find whether a particular element exist or not.
 The property that all the values lesser than the value of a node lies on the
left subtree and all the values greater than the value of a node lies on the
right subtree helps to perform the searching.
 Suppose we are on a node and the value to be searched is smaller than
the value of the node. In that case, we will search for the value in the left
subtree. Otherwise, if the value to be searched is larger, we will just search
the right subtree.
 Step 1 - Read the search element from the user.
 Step 2 - Compare the search element with the value of root node in the
tree.
 Step 3 - If both are matched, then display "Given node is found!!!" and
terminate the function
 Step 4 - If both are not matched, then check whether search element is
smaller or larger than that node value.
 Step 5 - If search element is smaller, then continue the search process in left
subtree.
 Step 6- If search element is larger, then continue the search process in right
subtree.
 Step 7 - Repeat the same until we find the exact element or until the search
element is compared with the leaf node
 Step 8 - If we reach to the node having the value equal to the search
value then display "Element is found" and terminate the function.
 Step 9 - If we reach to the leaf node and if it is also not matched with the
search element, then display "Element is not found" and terminate the
function.
Inserting a New Element

 Insert function is used to add a new element in a binary search tree at


appropriate location.

 Insert function is to be designed in such a way that, it must node violate the
property of binary search tree at each value.

 This is a very straight forward operation. First, the root node is inserted, then
the next value is compared with the root node.

 If the value is greater than root, it is added to the right subtree, and if it is
lesser than the root, it is added to the left subtree.
 Step 1 - Create a newNode with given value and set
its left and right to NULL.
 Step 2 - Check whether tree is Empty.
 Step 3 - If the tree is Empty, then set root to newNode.
 Step 4 - If the tree is Not Empty, then check whether the value of newNode
is smaller or larger than the node (here it is root node).
 Step 5 - If newNode is smaller than or equal to the node then move to
its left child. If newNode is larger than the node then move to its right child.
 Step 6- Repeat the above steps until we reach to the leaf node (i.e.,
reaches to NULL).
 Step 7 - After reaching the leaf node, insert the newNode as left child if the
newNode is smaller or equal to that leaf node or else insert it as right child.
Deleting a Node

 Delete is the most advanced and complex among all other operations.
There are multiple cases handled for deletion in the BST.
 Deleting a node from Binary search tree includes following three cases:

 Case 1: Deleting a Leaf node (A node with no children)

 Case 2: Deleting a node with one child

 Case 3: Deleting a node with two children


Case 1: Deleting a leaf node
 We use the following steps to delete a leaf node from BST...
 Step 1 - Find the node to be deleted using search operation
 Step 2 - Delete the node using free function (If it is a leaf) and terminate the
function.
Case 2: Deleting a node with one child
 We use the following steps to delete a node with one child from BST...
 Step 1 - Find the node to be deleted using search operation
 Step 2 - If it has only one child then create a link between its parent node
and child node.
 Step 3 - Delete the node using free function and terminate the function.
Case 3: Deleting a node with two
children
 We use the following steps to delete a node with two children from BST...
 Step 1 - Find the node to be deleted using search operation
 Step 2 - If it has two children, then find the largest node in its left
subtree (OR) the smallest node in its right subtree.
 Step 3 - Swap both deleting node and node which is found in the above
step.
 Step 4 - Then check whether deleting node came to case 1 or case 2 or
else goto step 2
 Step 5 - If it comes to case 1, then delete using case 1 logic.
 Step 6- If it comes to case 2, then delete using case 2 logic.
 Step 7 - Repeat the same process until the node is deleted from the tree.
 Construct a BST from the given list of elements
43, 10, 79, 90, 12, 54, 11, 9, 50
 Insert 43 into the tree as the root of the tree.
 Read the next element, if it is lesser than the root node element, insert it as the
root of the left sub-tree.
 Otherwise, insert it as the root of the right of the right sub-tree.
The process of creating BST by using the given elements, is shown in the image below.
Determine Height of a BST
 In order to determ ine the height of a binary search tree. We find the height of left
subtree and right subtree of a given node.
 The height of the binary search tree at a given node will be equal to maximum
height of the left and right subtree plus 1.
determineHeight( )
Begin
tree = root
if tree == NULL then
return 0
else
leftHeight = determineHeight(tree->left)
rightHeight = determineHeight(tree->right)
if ( leftHeight > rightHeight ) then
return ++leftHeight
else
return ++rightHeight
end
Determine Number of Nodes/Elements
 Any of the traversal schemes can be used to determine the number of elements
in binary search tree. However, we will use a different approach.
 Note that the number of elements/nodes in a binary search tree is equal to the
number of nodes in the left subtree plus the number of nodes in the right subtree
of a given node plus 1.
 Note that if the binary search tree is empty, then the number of nodes is equal
to 0.
totalNodes()
Begin
tree = root
if ( tree == NULL ) then
retrun 0
else
retrun (totalNodes(tree->left) + totalNodes(tree->right) + 1)
endif
End
Determine Number of Internal/Non-leaf
Nodes
 The number of internal/non-leaf nodes in a binary search tree is equal to
the number of internal/non-leaf nodes in the left subtree plus number of
non-leaf nodes in the right subtree of a given nodes plus 1.
internalNodes()
Begin
tree = root
if ((tree == NULL) || ((tree->left == NULL) && (tree->rigt == NULL))) then
return 0
else
return (internalNodes(tree->left) + internalNodes(tree->right) + 1)
Determine Number of External/Leaf
Nodes
 The number of external/leaf nodes in a tree is equal to the sum of the
external/leaf nodes in the left subtree and the external/leaf nodes in the
right subtree of a given nodes.

 Note that if the binary search tree is empty then the number of
external/non-leaf nodes is equal to 0.

 If there is only one node in the binary search tree then the number of
external/leaf nodes is equal to 1.
externalNodes()
Begin
tree = root
if ( tree == NULL ) then
retrun 0
else if ((tree->left == NULL) && (tree->right == NULL)) then
return 1
else
retrun (externalNodes(tree->left) + externalNodes(tree->right))
endif
End
AVL Tree

 AVL Tree is invented by GM Adelson - Velsky and EM Landis in 1962. The tree
is named AVL in honor of its inventors.
 AVL Tree can be defined as height balanced binary search tree in which
each node is associated with a balance factor which is calculated by
subtracting the height of its right sub-tree from that of its left sub-tree.
 Tree is said to be balanced if balance factor of each node is in between -1
to 1, otherwise, the tree will be unbalanced and need to be balanced.

Balance Factor (k) = height (left(k)) - height (right(k))


 If balance factor of any node is 1, it means that the left sub-tree is one level
higher than the right sub-tree.
 If balance factor of any node is 0, it means that the left sub-tree and right
sub-tree contain equal height.
 If balance factor of any node is -1, it means that the left sub-tree is one
level lower than the right sub-tree.
 An AVL tree is given in the following figure. We can see that, balance
factor associated with each node is in between -1 and +1. therefore, it is an
example of AVL tree.
Here we see that the first tree is balanced and the next two trees are not
balanced.

In the second tree, the left subtree of C has height 2 and the right subtree has
height 0, so the difference is 2. In the third tree, the right subtree of A has height
2 and the left is missing, so it is 0, and the difference is 2 again. AVL tree
permits difference (balance factor) to be only 1.
Operations on AVL tree
 Due to the fact that, AVL tree is also a binary search tree therefore, all the
operations are performed in the same way as they are performed in a
binary search tree.
 Searching and traversing do not lead to the violation in property of AVL
tree.
 However, insertion and deletion are the operations which can violate this
property and therefore, they need to be revisited.
 Insertion : Insertion in AVL tree is performed in the same way as it is
performed in a binary search tree. However, it may lead to violation in the
AVL tree property and therefore the tree may need balancing. The tree
can be balanced by applying rotations.
 Deletion : Deletion can also be performed in the same way as it is
performed in a binary search tree. Deletion may also disturb the balance of
the tree therefore, various types of rotations are used to rebalance the tree.
AVL Rotations

 We perform rotation in AVL tree only in case if Balance Factor is other than -
1, 0, and 1. There are basically four types of rotations which are as follows:

 LL rotation: Inserted node is in the left subtree of left subtree of A


 RR rotation : Inserted node is in the right subtree of right subtree of A
 LR rotation : Inserted node is in the right subtree of left subtree of A
 RL rotation : Inserted node is in the left subtree of right subtree of A

 Where node A is the node whose balance Factor is other than -1, 0, 1.
 The first two rotations LL and RR are single rotations and the next two
rotations LR and RL are double rotations.
LL Rotation
 When BST becomes unbalanced, due to a node is inserted into the left
subtree of the left subtree of C, then we perform LL rotation, LL rotation is
clockwise rotation, which is applied on the edge below a node having
balance factor 2.

 In above example, node C has balance factor 2 because a node A is


inserted in the left subtree of C left subtree. We perform the LL rotation on
the edge below A.
Example
 Insert the node with value 12 into the tree shown in the following figure.
RR Rotation
 When BST becomes unbalanced, due to a node is inserted into the right
subtree of the right subtree of A, then we perform RR rotation, RR rotation is
an anticlockwise rotation, which is applied on the edge below a node
having balance factor -2

 In above example, node A has balance factor -2 because a node C is


inserted in the right subtree of A right subtree. We perform the RR rotation
on the edge below A.
Example
 Insert 100 into the AVL Tree shown in the figure.
LR Rotation
 Double rotations are bit tougher than single rotation which has already
explained above.
 LR rotation = RR rotation + LL rotation, i.e., first RR rotation is performed on
subtree and then LL rotation is performed on full tree, by full tree we mean
the first node from the path of inserted node whose balance factor is other
than -1, 0, or 1.

 A node B has been inserted into the right subtree of A the


left subtree of C, because of which C has become an
unbalanced node having balance factor 2. This case is L R
rotation where: Inserted node is in the right subtree of left
subtree of C
As LR rotation = RR + LL rotation, hence RR (anticlockwise) on
subtree rooted at A is performed first. By doing RR rotation,
node A, has become the left subtree of B.

After performing RR rotation, node C is still unbalanced, i.e., having


balance factor 2, as inserted node A is in the left of left of C

Now we perform LL clockwise rotation on full tree, i.e. on node C.


node C has now become the right subtree of node B, A is left subtree of
B

Balance factor of each node is now either -1, 0, or 1, i.e. BST is


balanced now.
Example
 Insert the node with value 70 into the tree shown in the following data
structure.
RL Rotation

 R L rotation = LL rotation + RR rotation, i.e., first LL rotation is performed on


subtree and then RR rotation is performed on full tree, by full tree we mean
the first node from the path of inserted node whose balance factor is other
than -1, 0, or 1.

A node B has been inserted into the left subtree of C the right subtree
of A, because of which A has become an unbalanced node having
balance factor - 2. This case is RL rotation where: Inserted node is in
the left subtree of right subtree of A
As RL rotation = LL rotation + RR rotation, hence, LL (clockwise) on
subtree rooted at C is performed first. By doing RR rotation, node C has
become the right subtree of B.

After performing LL rotation, node A is still unbalanced, i.e. having


balance factor -2, which is because of the right-subtree of the right-
subtree node A.

Now we perform RR rotation (anticlockwise rotation) on full tree, i.e. on


node A. node C has now become the right subtree of node B, and node A
has become the left subtree of B.

Balance factor of each node is now either -1, 0, or 1, i.e., BST is


balanced now.
Insert 90 into the AVL Tree shown in the figure.
Construct an AVL tree having the following elements
H, I, J, B, A, E, C, F, D, G, K, L
Deletion in AVL Trees

 Deletion is also very straight forward. We delete using the same logic as in
simple binary search trees. After deletion, we restructure the tree, if
needed, to maintain its balanced height.
 Step 1: Find the element in the tree.
 Step 2: Delete the node, as per the BST Deletion.
 Step 3: Two cases are possible:-
 Case 1: Deleting from the right subtree.

 1A. If BF(node) = +2 and BF(node -> left-child) = +1, perform LL rotation.


 1B. If BF(node) = +2 and BF(node -> left-child) = -1, perform LR rotation.
 1C. If BF(node) = +2 and BF(node -> left-child) = 0, perform LL rotation.
 Case 2: Deleting from left subtree.
 2A. If BF(node) = -2 and BF(node -> right-child) = -1, perform RR rotation.
 2B. If BF(node) = -2 and BF(node -> right-child) = +1, perform RL rotation.
 2C. If BF(node) = -2 and BF(node -> right-child) = 0, perform RR rotation.
 Construct an AVL tree from given list of elements
1, 2, 3, 4, 5, 6, 7, 8, 9, 10
Threaded Binary Tree

 On carefully, examining the linked list representation of a binary tree T, you


will find that approximately half of the pointer fields (left and right pointer
fields that holds the address of the left child and right child, respectively)
contain NULL entries in these fields.
 The space occupied by these NULL entries can be utilized to store some
kind of valuable information.
 One possible way to utilize this space is that we can store special pointer
that point to nodes higher in the tree, i.e. ancestors.
 These special pointers are called threads, and the binary tree having such
pointers is called a threaded binary tree.
 Threads in a binary tree must be distinguished from normal pointers, In the
graphical representation of a threaded binary tree, the threads are shown
by dotted lines.
 There are three ways to thread a binary tree:
 The right NULL pointer of each node can be replaced by a thread to the
successor of that node under in-order traversal called right thread, and the tree
will called a right threaded tree.
 The left NULL pointer of each node can be replaced by a thread to the
predecessor of that node under in order traversal called left thread, and the tree
will called a left threaded tree.

 Both left and right NULL pointers can be used to point to predecessor and
successor of that node respectively, under in order traversal. Such a tree is called
a fully threaded tree.

 A threaded binary tree where only one thread is used is also known as one
way threaded tree and where both threads are used is also known as two
way threaded tree.
M-Way Tree

 A multi way tree is defined as a tree that can have more than two children.
If a multi way tree can have maximum m children, then this tree is called as
multi way tree of order m (or an m-way tree).
 The nodes in an m-way tree will be made up of m-1 key fields and pointers
to children.
 To make the processing of m-way trees easier some type of constraint or
order will be imposed on the keys within each node, resulting in a mult iway
search tree of order m (or an m-way search tree).
 By definition an m-way search tree is a m-way tree in which following
condition should be satisfied −
 Each node is associated with m children and m-1 key fields
 The keys in each node are arranged in ascending order.
 The keys in the first j children are less than the j-th key.
 The keys in the last m-j children are higher than the j-th key.
B Tree
 B Tree is a specialized m-way tree that can be widely used for disk access.
 A B-Tree of order m can have at most m-1 keys and m children.
 One of the main reason of using B tree is its capability to store large number
of keys in a single node and large key values by keeping the height of the
tree relatively small.
 A B tree of order m contains all the properties of an M way tree. In addition,
it contains the following properties.
 Every node in B-Tree will hold maximum m children
 Every node except root and leaves, can hold at least m/2 children
 The root nodes must have at least two children.
 All leaf nodes must have at same level

 It is not necessary that, all the nodes contain the same number of children but, each
node must have m/2 number of nodes.
 A B tree of order 3 (2-3 Tree) is shown in the following image.
Insertion in B-Tree
 Inserting an element on a B-tree consists of two events: searching the
appropriate node to insert the element and splitting the node if required.
Insertion operation always takes place in the bottom-up approach.
 Steps:
 If the tree is empty, allocate a root node and insert the key.
 Update the allowed number of keys in the node.
 Search the appropriate node for insertion.
 If the node is full, follow the steps below.
 Insert the elements in increasing order.
 Now, there are elements greater than its limit. So, split at the median.
 Push the median key upwards and make the left keys as a left child and the right
keys as a right child.
 If the node is not full, follow the steps below.
 Insert the node in increasing order.
 The elements to be inserted are 8, 9, 10, 11, 15, 16, 17, 18, 20, 23.
Searching
 Searching for an element in a B-tree is the generalized form of searching an
element in a Binary Search Tree. The following steps are followed.

 Starting from the root node, compare k with the first key of the node.
If k = the first key of the node, return the node and the index.
 If k.leaf = true, return NULL (i.e. not found).
 If k < the first key of the root node, search the left child of this key recursively.
 If there is more than one key in the current node and k > the first key, compare k
with the next key in the node. If k < next key, search the left child of this key (ie. k
lies in between the first and the second keys). Else, search the right child of the
key.
 Repeat steps 1 to 4 until the leaf is reached.
Searching Example
 Let us search key k = 17 in the tree below of degree 3.

 k is not found in the root so, compare it with the root key
 Since k > 11, go to the right child of the root node

 Compare k with 16. Since k > 16, compare k with the next key 18
 Since k < 18, k lies between 16 and 18. Search in the right child of 16 or the
left child of 18.

 k is found.
Deletion from a B-tree

 Deleting an element on a B-tree consists of three main events: searching


the node where the key to be deleted exists, deleting the key and
balancing the tree if required.
 While deleting a tree, a condition called underflow may occur. Underflow
occurs when a node contains less than the minimum number of keys it
should hold.
 The terms to be understood before studying deletion operation are:
 In-order Predecessor
 The largest key on the left child of a node is called its in-order predecessor.
 In-order Successor
 The smallest key on the right child of a node is called its in-order successor.
Deletion Operation

 Before going through the steps below, one must know these facts about a
B tree of degree m.
 A node can have a maximum of m children. (i.e. 3)
 A node can contain a maximum of m - 1 keys. (i.e. 2)
 A node should have a minimum of ⌈m/2⌉ children. (i.e. 2)
 A node (except root node) should contain a minimum of ⌈m/2⌉ - 1 keys. (i.e.
1)
There are three main cases for deletion
operation in a B tree.
 Case I
 The key to be deleted lies in the leaf. There are two cases for it.
a) The deletion of the key does not violate the property of the minimum number of
keys a node should hold.

In the tree below, deleting 32 does not violate the above properties.
b) The deletion of the key violates the property of the minimum number of keys a
node should hold. In this case, we borrow a key from its immediate neighboring
sibling node in the order of left to right.
 First, visit the immediate left sibling. If the left sibling node has more than a
minimum number of keys, then borrow a key from this node.
 Else, check to borrow from the immediate right sibling node.

In the tree below, deleting 31 results in the above condition. Let us borrow a key
from the left sibling node.
 If both the immediate sibling nodes already have a minimum number of
keys, then merge the node with either the left sibling node or the right
sibling node. This merging is done through the parent node.
 Deleting 30 results in the above case.
 Case II
 If the key to be deleted lies in the internal node, the following cases occur.
a) The internal node, which is deleted, is replaced by an in-order predecessor if the
left child has more than the minimum number of keys.
b) The internal node, which is deleted, is replaced by an in-order successor if
the right child has more than the minimum number of keys.
c) If either child has exactly a minimum number of keys then, merge the left
and the right children.
After merging if the parent node has less than the minimum number of keys
then, look for the siblings as in Case I.
 Case III

 In this case, the height of the tree shrinks.


 If the target key lies in an internal node, and the deletion of the key leads
to a fewer number of keys in the node (i.e. less than the minimum required),
then look for the in-order predecessor and the in-order successor.
 If both the children contain a minimum number of keys then, borrowing
cannot take place. This leads to Case II(3) i.e. merging the children.
 Again, look for the sibling to borrow a key. But, if the sibling also has only a
minimum number of keys then, merge the node with the sibling along with
the parent. Arrange the children accordingly (increasing order).
B+ Tree

 B+ Tree is an extension of B Tree which allows efficient insertion, deletion


and search operations.
 In B Tree, Keys and records both can be stored in the internal as well as leaf
nodes. Whereas, in B+ tree, records (data) can only be stored on the leaf
nodes while internal nodes can only store the key values.
 The leaf nodes of a B+ tree are linked together in the form of a singly linked
lists to make the search queries more efficient.
 B+ Tree are used to store the large amount of data which can not be
stored in the main memory. Due to the fact that, size of main memory is
always limited, the internal nodes (keys to access records) of the B+ tree
are stored in the main memory whereas, leaf nodes are stored in the
secondary memory.
 The internal nodes of B+ tree are often called index nodes. A B+ tree of
order 3 is shown in the following figure.
Insertion in B+ Tree

 Step 1: Insert the new node as a leaf node

 Step 2: If the leaf doesn't have required space, split the node and copy the
middle node to the next index node.

 Step 3: If the index node doesn't have required space, split the node and
copy the middle element to the next index page.
Example :
 Insert the value 195 into the B+ tree of order 5 shown in the following figure.

 195 will be inserted in the right sub-tree of 120 after 190. Insert it at the
desired position.
 The node contains greater than the maximum number of elements i.e. 4,
therefore split it and place the median node up to the parent.

 Now, the index node contains 6 children and 5 keys which violates the B+
tree properties, therefore we need to split it, shown as follows.
Deletion in B+ Tree

 Step 1: Delete the key and data from the leaves.

 Step 2: if the leaf node contains less than minimum number of elements,
merge down the node with its sibling and delete the key in between them.

 Step 3: if the index node contains less than minimum number of elements,
merge the node with the sibling and move down the key in between them.
Example
 Delete the key 200 from the B+ Tree shown in the following figure.

 200 is present in the right sub-tree of 190, after 195. delete it.
 Merge the two nodes by using 195, 190, 154 and 129.

 Now, element 120 is the single element present in the node which is
violating the B+ Tree properties. Therefore, we need to merge it by using 60,
78, 108 and 120.
 Now, the height of B+ tree will be decreased by 1.
Heap Data Structure

 Heap data structure is a complete binary tree that satisfies the heap
property. It is also called as a binary heap.
 A complete binary tree is a special binary tree in which
 Every level, except possibly the last, is filled
 Heap Property is the property of a node in which
 (for max heap) key of each node is always greater than its child node/s and the
key of the root node is the largest among all other nodes;

 (for min heap) key of each node is always smaller than the child node/s and the
key of the root node is the smallest among all other nodes.

 A heap is represented in memory using linear array, i.e. by sequential


representation.
Heap Operations
Heapify
 Heapify is the process of creating a heap data structure from a binary tree.
It is used to create a Min-Heap or a Max-Heap.

 Let the input array be


 Create a complete binary tree from the array

 Start from the first index of non-leaf node whose index is given by n/2 – 1.
 Set current element i as largest.
 The index of left child is given by 2i + 1 (when starting index is 0) or 2i (when
starting index is 1) and the right child is given by 2i + 2 (when starting index
is 0) or 2i+1 (when starting index is 1).
 If leftChild is greater than currentElement (i.e. element at ith index),
set leftChildIndex as largest.
If rightChild is greater than element in largest, set rightChildIndex as largest.
 Swap largest with currentElement

 Repeat steps 3-7 until the subtrees are also heapified.


Insert Element into Heap

 Algorithm for insertion in Max Heap


 Insert the new element at the end of the tree

 Heapify the tree.


Delete Element from Heap

 Algorithm for deletion in Max Heap


 Select the element to be deleted.

 Swap it with the last element.


 Remove the last element.

 Heapify the tree.


Heap Sort

 The general approach of heap sort is as follows:

 From the given array. build the initial max heap.


 Interchange the root (maximum) element with the last element.
 Use reheapify operation from root node to rebuild the heap of size one less that
the starting.
 Repeat steps 1 and 2 until there are no more elements.
 After building max-heap, the elements in the array Arr will be:

Step 1: 8 is swapped with 5.


 Step 2: 8 is disconnected from heap as 8 is in correct position now and.

 Step 3: Max-heap is created and 7 is swapped with 3.


 Step 4: 7 is disconnected from heap.

 Step 5: Max heap is created and 5 is swapped with 1.


 Step 6: 5 is disconnected from heap.

 Step 7: Max heap is created and 4 is swapped with 3.


 Step 8: 4 is disconnected from heap.

 Step 9: Max heap is created and 3 is swapped with 1.


 Step 10: 3 is disconnected.

 After all the steps, we will get a sorted array.


Graph

 A graph can be defined as group of vertices and edges that are used to
connect these vertices.
 A graph can be seen as a cyclic tree, where the vertices (Nodes) maintain
any complex relationship among them instead of having parent child
relationship.
 A graph G can be defined as an ordered set G(V, E) where V(G) represents
the set of vertices and E(G) represents the set of edges which are used to
connect these vertices.
 A Graph G(V, E) with 5 vertices (A, B, C, D, E) and six edges ((A,B), (B,C),
(C,E), (E,D), (D,B), (D,A)) is shown in the following figure.
Directed and Undirected Graph

 A graph can be directed or undirected.


 However, in an undirected graph, edges are not associated with the
directions with them. An undirected graph is shown in the above figure
since its edges are not attached with any of the directions. If an edge exists
between vertex A and B then the vertices can be traversed from B to A as
well as A to B.
 In a directed graph, edges form an ordered pair. Edges represent a
specific path from some vertex A to another vertex B. Node A is called
initial node while node B is called terminal node.
Graph Terminology

 Path
 A path can be defined as the sequence of nodes that are followed in order to
reach some terminal node V from the initial node U.
 Closed Path
 A path will be called as closed path if the initial node is same as term inal node. A
path will be closed path if V0=VN.

 Simple Path
 If all the nodes of the graph are distinct with an exception V0=VN, then such path
P is called as closed simple path.
 Cycle
 A cycle can be defined as the path which has no repeated edges or vertices
except the first and last vertices.
 Connected Graph
 A connected graph is the one in which some path exists between every two
vertices (u, v) in V. There are no isolated nodes in connected graph.
 Complete Graph
 A complete graph is the one in which every node is connected with all other
nodes. A complete graph contain n(n-1)/2 edges where n is the number of
nodes in the graph.
 Weighted Graph
 In a weighted graph, each edge is assigned with some data such as length or
weight. The weight of an edge e can be given as w(e) which must be a positive
(+) value indicating the cost of traversing the edge.
 Digraph
 A digraph is a directed graph in which each edge of the graph is associated
with some direction and the traversing can be done only in the specified
direction.
 Loop
 An edge that is associated with the similar end points can be called as Loop.

 Adjacent Nodes
 If two nodes u and v are connected via an edge e, then the nodes u and v are
called as neighbors or adjacent nodes.

 Degree of the Node


 A degree of a node is the number of edges that are connected with that node.
A node with degree 0 is called as isolated node.
Graph Representation

 By Graph representation, we simply mean the technique which is to be


used in order to store some graph into the computer's memory.

 There are two ways to store Graph into the computer's memory.

 Sequential Representation or Adjacency Matrix representation


 Linked Representation or Adjacency List Representation
Adjacency Matrix representation

 In sequential representation, we use adjacency matrix to store the


mapping represented by vertices and edges.

 In adjacency matrix, the rows and columns are represented by the graph
vertices. A graph having n vertices, will have a dimension n x n.

 An entry M ij in the adjacency matrix representation of an undirected graph


G will be 1 if there exists an edge between Vi and Vj.
 An undirected graph and its adjacency matrix representation is shown in
the following figure.

 in the above figure, we can see the mapping among the vertices (A, B, C,
D, E) is represented by using the adjacency matrix which is also shown in
the figure.
 There exists different adjacency matrices for the directed and undirected
graph. In directed graph, an entry Aij will be 1 only when there is an edge
directed from Vi to Vj.
 A directed graph and its adjacency matrix representation is shown in the
following figure.

 Representation of weighted directed graph is different. Instead of filling the


entry by 1, the Non- zero entries of the adjacency matrix are represented
by the weight of respective edges.
 The weighted directed graph along with the adjacency matrix
representation is shown in the following figure.
Linked Representation

 In the linked representation, an adjacency list is used to store the Graph


into the computer's memory.
 Consider the undirected graph shown in the following figure and check the
adjacency list representation.
 An adjacency list is maintained for each node present in the graph which
stores the node value and a pointer to the next adjacent node to the
respective node.
 If all the adjacent nodes are traversed then store the NULL in the pointer
field of last node of the list. The sum of the lengths of adjacency lists is equal
to the twice of the number of edges present in an undirected graph.
 Consider the directed graph shown in the following figure and check the
adjacency list representation of the graph.
 In a directed graph, the sum of lengths of all the adjacency lists is equal to
the number of edges present in the graph.
 In the case of weighted directed graph, each node contains an extra field
that is called the weight of the node. The adjacency list representation of a
directed graph is shown in the following figure.
Graph Traversal Algorithm

 Traversing the graph means examining all the nodes and vertices of the
graph. There are two standard methods by using which, we can traverse
the graphs.

 Breadth First Search

 Depth First Search


Breadth First Search (BFS) Algorithm

 Breadth first search is a graph traversal algorithm that starts traversing the
graph from root node and explores all the neighboring nodes.
 Then, it selects the nearest node and explore all the unexplored nodes.
 The algorithm follows the same process for each of the nearest node until it
finds the goal.
 The algorithm of breadth first search is given below. The algorithm starts with
examining the node A and all of its neighbors.
 In the next step, the neighbors of the nearest node of A are explored and
process continues in the further steps.
 The algorithm explores all neighbors of all the nodes and ensures that each
node is visited exactly once and no node is visited twice.
BFS Algorithm

 Step 1: SET STATUS = 1 (ready state) for each node in G


 Step 2: Enqueue the starting node A and set its STATUS = 2 (waiting state)
 Step 3: Repeat Steps 4 and 5 until QUEUE is empty
 Step 4: Dequeue a node N. Process it and set its STATUS = 3 (processed
state).
 Step 5: Enqueue all the neighbors of N that are in the ready state (whose
STATUS = 1) and set their STATUS = 2 (waiting state)
[END OF LOOP]
 Step 6: EXIT
BreadthFirstSearch(adj, n, s)
Begin

for I = 1 to n by 1 do
set color[I] = WHITE

endfor

Set color[s] = GRAY

Call CreateQueue(q);

Call Enqueue(q, s);

while( q is not empty )


Set u = Dequeue(q)

Set ptr = adj(u)

while(ptr!= NULL) do
if ( color[ptr->info] = WHITE) then

Set color[ptr->info] = GRAY


Call Enqueue(q, ptr->info)

endif

endwhile

Print: u
Set color[u] = Black

endwhile

End
 The vertices are traversed in following manner
2 1 5 4 3 6
Depth First Search (DFS)

 Depth first search (DFS) algorithm starts with the initial node of the graph G,
and then goes to deeper and deeper until we find the goal node or the
node which has no children.

 The algorithm, then backtracks from the dead end towards the most recent
node that is yet to be completely unexplored.

 The data structure which is being used in DFS is stack. The process is similar
to BFS algorithm.
 Step 1: SET STATUS = 1 (ready state) for each node in G
 Step 2: Push the starting node A on the stack and set its STATUS = 2 (waiting
state)
 Step 3: Repeat Steps 4 and 5 until STACK is empty
 Step 4: Pop the top node N. Process it and set its STATUS = 3 (processed
state)
 Step 5: Push on the stack all the neighbors of N that are in the ready state
(whose STATUS = 1) and set their
STATUS = 2 (waiting state)
[END OF LOOP]
 Step 6: EXIT
DepthFirstSearch(adj, n, s)
Begin
for I = 1 to n by 1 do
Set color[i] = WHITE
endfor
Set color[s] = GRAY
Call createstack(stack)
Call push(stack, s)
while(stack is not empty) do
Set u = peek(stack)
Set ptr = adj[u]
while( ptr != NULL ) do
if (color[ptr->info] = WHITE) then
Set color[ptr->info] = GRAY
Call push(&stack, ptr->info)
Set u = ptr->info
Set ptr = adj[u]
else
Set ptr = ptr ->next
endif
endwhile
Call pop(&stack, u)
Print: u
Set color[u] = BLACK
endwhile
End
 The vertices are traversed in following manner
3 6 5 4 1 2
Spanning Tree

 A spanning tree is a subset of Graph G, which has all the vertices covered
with minimum possible number of edges. Hence, a spanning tree does not
have cycles and it cannot be disconnected.

 By this definition, we can draw a conclusion that every connected and


undirected Graph G has at least one spanning tree. A disconnected graph
does not have any spanning tree, as it cannot be spanned to all its
vertices.
We found three spanning trees off one complete graph. A complete undirected
graph can have maximum nn-2 number of spanning trees, where n is the number of
nodes. In the above addressed example, n is 3, hence 33−2 = 3 spanning trees are
possible.
General Properties of Spanning Tree

 We now understand that one graph can have more than one spanning
tree. Following are a few properties of the spanning tree connected to
graph G −
 A connected graph G can have more than one spanning tree.
 All possible spanning trees of graph G, have the same number of edges and
vertices.
 The spanning tree does not have any cycle (loops).
 Removing one edge from the spanning tree will make the graph disconnected,
i.e. the spanning tree is minimally connected.
 Adding one edge to the spanning tree will create a circuit or loop, i.e. the
spanning tree is maximally acyclic.
Minimum Spanning Tree (MST)

 In a weighted graph, a minimum spanning tree is a spanning tree that has


minimum weight than all other spanning trees of the same graph.
 In real-world situations, this weight can be measured as distance,
congestion, traffic load or any arbitrary value denoted to the edges.
 The minimum spanning tree from a graph is found using the following
algorithms:

 Kruskal's Algorithm
 Prim's Algorithm
Kruskal's Spanning Tree Algorithm
 Kruskal's algorithm to find the minimum cost spanning tree uses the greedy
approach.
 This algorithm treats the graph as a forest and every node it has as an
individual tree.
 A tree connects to another only and only if, it has the least cost among all
available options and does not violate MST properties.
 To understand Kruskal's algorithm let us consider the following example −
 Step 1 - Remove all loops and Parallel Edges
 Remove all loops and parallel edges from the given graph.

 In case of parallel edges, keep the one which has the least cost associated and
remove all others.
 Step 2 - Arrange all edges in their increasing order of weight
 The next step is to create a set of edges and weight, and arrange them in an
ascending order of weight (cost).

 Step 3 - Add the edge which has the least weight


 Now we start adding edges to the graph beginning from the one which has the least weight.
Throughout, we shall keep checking that the spanning properties remain intact.
 In case, by adding one edge, the spanning tree property does not hold then we shall
consider not to include the edge in the graph.
 The least cost is 2 and edges involved are B,D and D,T. We add them.
Adding them does not violate spanning tree properties, so we continue to
our next edge selection.
 Next cost is 3, and associated edges are A,C and C,D. We add them again

 Next cost in the table is 4, and we observe that adding it will create a
circuit in the graph.

 We ignore it. In the process we shall ignore/avoid all edges that create a
circuit.
 We observe that edges with cost 5 and 6 also create circuits. We ignore
them and move on.

 Now we are left with only one node to be added. Between the two least
cost edges available 7 and 8, we shall add the edge with cost 7.

 By adding edge S,A we have included all the nodes of the graph and we
now have minimum cost spanning tree.
Algorithm Steps:
 Sort the graph edges with respect to their weights.
 Start adding edges to the MST from the edge with the smallest weight until
the edge of the largest weight.
 Only add edges which doesn't form a cycle , edges which connect only
disconnected components.

Sort all edges in the graph G in the order of their increasing weights;
repeat V-1 times // as MST contains V-1 edges
{
Select the next edge with minimum weight from the graph G;

if (no cycle is formed by adding the edge in MST i.e. the edge connects
two different connected components in MST)

add the edge to MST;


}
Prim's Spanning Tree Algorithm
 Prim's algorithm to find minimum cost spanning tree (as Kruskal's algorithm)
uses the greedy approach. Prim's algorithm shares a similarity with
the shortest path first algorithms.
 Prim's algorithm, in contrast with Kruskal's algorithm, treats the nodes as a
single tree and keeps on adding new nodes to the spanning tree from the
given graph.
 To contrast with Kruskal's algorithm and to understand Prim's algorithm
better, we shall use the same example −
 Step 1 - Remove all loops and parallel edges

 Remove all loops and parallel edges from the given graph. In case of
parallel edges, keep the one which has the least cost associated and
remove all others.
 Step 2 - Choose any arbitrary node as root node
 In this case, we choose S node as the root node of Prim's spanning tree.
 This node is arbitrarily chosen, so any node can be the root node.

 Step 3 - Check outgoing edges and select the one with less cost
 After choosing the root node S, we see that S,A and S,C are two edges with
weight 7 and 8, respectively. We choose the edge S,A as it is lesser than the
other.
 Now, the tree S-7-A is treated as one node and we check for all edges
going out from it. We select the one which has the lowest cost and include
it in the tree.

 After this step, S-7-A-3-C tree is formed. Now we'll again treat it as a node
and will check all the edges again. However, we will choose only the least
cost edge. In this case, C-3-D is the new edge, which is less than other
edges' cost 8, 6, 4, etc.
 After adding node D to the spanning tree, we now have two edges going
out of it having the same cost, i.e. D-2-T and D-2-B. Thus, we can add either
one. But the next step will again yield edge 2 as the least cost. Hence, we
are showing a spanning tree with both edges included.

 We may find that the output spanning tree of the same graph using two
different algorithms is same.
Algorithm

 Step 1: Select a starting vertex


 Step 2: Repeat Steps 3 and 4 until there are fringe vertices
 Step 3: Select an edge e connecting the tree vertex and fringe vertex that
has minimum weight
 Step 4: Add the selected edge and the vertex to the minimum spanning
tree T
[END OF LOOP]
 Step 5: EXIT
Dijkstra's Algorithm

 Dijkstra's algorithm allows us to find the shortest path between any two
vertices of a graph.
 It differs from the minimum spanning tree because the shortest distance
between two vertices might not include all the vertices of the graph.
 It is a greedy algorithm that solves the single-source shortest path problem
for a directed graph G = (V, E) with nonnegative edge weights, i.e., w (u, v)
≥ 0 for each edge (u, v) ∈ E.
 Dijkstra's Algorithm maintains a set S of vertices whose final shortest - path
weights from the source s have already been determined. That's for all
vertices v ∈ S; we have d [v] = δ (s, v). The algorithm repeatedly selects the
vertex u ∈ V - S with the minimum shortest - path estimate, insert u into S and
relaxes all edges leaving u.
 Because it always chooses the "lightest" or "closest" vertex in V - S to insert
into set S, it is called as the greedy strategy.
Dijkstra's Algorithm (G, w, s)
1. INITIALIZE - SINGLE - SOURCE (G, s)
2. S←∅
3. Q←V [G]
4. while Q ≠ ∅
5. do u ← EXTRACT - MIN (Q)
6. S ← S ∪ {u}
7. for each vertex v ∈ Adj [u]
8. do RELAX (u, v, w)
Example

 Step1: Q =[s, t, x, y, z]
We scanned vertices one by one and find out its adjacent. Calculate the
distance of each adjacent to the source vertices.
We make a stack, which contains those vertices which are selected after
computation of shortest distance.
Firstly we take 's' in stack M (which is a source)
M = [S] Q = [t, x, y, z]
 Step 2: Now find the adjacent of s that are t and y.
Adj [s] → t, y [Here s is u and t and y are v]

Case - (i) s → t
d [v] > d [u] + w [u, v]
d [t] > d [s] + w [s, t]
∞ > 0 + 10 [false condition]
Then d [t] ← 10

Adj [s] ← t, y

Case - (ii) s→ y
d [v] > d [u] + w [u, v]
d [y] > d [s] + w [s, y]
∞>0+5 [false condition]
∞>5
Then d [y] ← 5
By comparing case (i) and case (ii)

Adj [s] → t = 10, y = 5

y is shortest
y is assigned in 5 = [s, y]
 Step 3: Now find the adjacent of y that is t, x, z.
Adj [y] → t, x, z [Here y is u and t, x, z are v]
Case - (i) y →t
d [v] > d [u] + w [u, v]
d [t] > d [y] + w [y, t]
10 > 5 + 3
10 > 8
Then d [t] ← 8

Case - (ii) y → x
d [v] > d [u] + w [u, v]
d [x] > d [y] + w [y, x]
∞>5+9
∞ > 14
Then d [x] ← 14
Case - (iii) y → z
d [v] > d [u] + w [u, v]
d [z] > d [y] + w [y, z]
∞>5+2
∞>7
Then d [z] ← 7

By comparing case (i), case (ii) and case (iii)

Adj [y] → x = 14, t = 8, z =7

z is shortest

z is assigned in 7 = [s, z]
 Step - 4 Now we will find adj [z] that are s, x
Adj [z] → [x, s] [Here z is u and s and x are v]

Case - (i) z → x
d [v] > d [u] + w [u, v]
d [x] > d [z] + w [z, x]
14 > 7 + 6
14 > 13
Then d [x] ← 13

Case - (ii) z → s
d [v] > d [u] + w [u, v]
d [s] > d [z] + w [z, s]
0>7+7
∴ This condition does not satisfy so it will be discarded.
Now we have x = 13.
 Step 5: Now we will find Adj [t]
Adj [t] → [x, y] [Here t is u and x and y are v]

Case - (i) t → x
d [v] > d [u] + w [u, v]
d [x] > d [t] + w [t, x]
13 > 8 + 1
13 > 9
Then d [x] ← 9

Case - (ii) t → y
d [v] > d [u] + w [u, v]
d [y] > d [t] + w [t, y]
5 > 10
∴ This condition does not satisfy so it will be discarded.
 Thus we get all shortest path vertex as
Weight from s to y is 5
Weight from s to z is 7
Weight from s to t is 8
Weight from s to x is 9
These are the shortest distance from the source's' in the given graph.
Hash Table

 Hash Table is a data structure which stores data in an associative manner


means the location of data is determined directly as a function of the data
itself.
 In a hash table, data is stored in an array format, where each data value
has its own unique index value. Access of data becomes very fast if we
know the index of the desired data.
 Thus, it becomes a data structure in which insertion and search operations
are very fast irrespective of the size of the data.
 Hash Table uses an array as a storage medium and uses hash technique to
generate an index where an element is to be inserted or is to be located
from.
Hash function

 A hash function h is simply a mathematical formula that manipulates the


key in some form to compute the index for this key in the hash table.
 For example, a hash function can divide the key by some number, usually
the size of the hash table, and return remainder as the index of the key.

 Different hash functions:


 Division Method
 Multiplication method
 Midsquare method
 Folding Method
Division Method

 In division method, key k to be mapped into one of the m slots in the hash
table is divided by m and the remainder of the division is taken as index into
the hash table.
 The hash function is
h(k) = k mod m

Example: Consider hash table with 9 slots i.e m = 9 will map the key 132 to slot
7 since
h(132) = 132 mod 9 = 7
Multiplication Method

 The multiplication method operates in two steps.


 In the first step, the key value k is multiplied by a constant A in the range 0 <
A < 1 and extract the fractional part of value kA.
 In the second step, the fractional value obtained above is multiplied by m
and the floor of the result is taken as the hash value.
 The hash function is:
h(k) = | m ( k A mod 1) |
 Consider a hash table 10000 slots i.e. m = 10000, then the hash function will
map the key 123456 to slot 41 since

h(123456) = | 10000 * (123456 * 0.61803 mod 1) |


= | 10000 * (76300.0041151 mode 1) |
= | 10000 * 0.0041151 |
= | 41.151 |
= 41
Midsquare Method

 The midsquare method also operates in two steps.


 In the first step, the square of the key value k is taken.
 In the second step, the hash value is obtained by deleting digits from ends
of the squared value i.e. k^2.
 It is important to note that same position of k^2 must be used for all keys.
 Thus the hash function is
h(k) = s
 Where s is obtained by deleting digits from both sides of k^2.
 Consider a hash table with 100 slots i.e. m = 100, and key values k = 3205,
7148.

k: 3205 7148
k^2: 10272025 51093904
h(k): 72 93

The hash value are obtained by taking fourth and fifth digits counting from
right.
Folding Method

 The folding method also operates in two steps.

 In the first step, the key value k is divided into number of parts, k1, k2, k3,
……… kr, where each part has the same number of digits except the last
part. Which can have lesser digits.

 In the second step, these parts are added together and the hash value is
obtained by ignoring the last carry, if any.
 Consider a hash table with 100 slots i.e. m = 100, and key values k = 9235,
714, 71458.

 The calculations are shown below:

k: 9235 714 71458


Parts: 92, 35 71, 4 71, 45, 8
Sum of parts: 127 75 114
H(k) : 27 75 14
Example
 Let's assume that we have a list with a fixed size of 3 and the following
values
[1,2,3]
 We can use the above formula to calculate the positions that each value
should occupy.
 The following image shows the available indexes in our hash table.
 Step 1)
Calculate the position that will be occupied by the first value like so
h(1) = 1 % 3
=1
The value 1 will occupy the space on index 1
 Step 2)
Calculate the position that will be occupied by the second value
h(2) = 2 % 3
=2
The value 2 will occupy the space on index 2
 Step 3)
Calculate the position that will be occupied by the third value.
h(3) = 3 % 3
=0
The value 3 will occupy the space on index 0
 Final Result
Our filled in hash table will now be as follows.
Collision

 A collision occurs when the algorithm generates the same hash for more
than one value.

 Let's look at an example.


 Suppose we have the following list of values
[3,2,9,11,7]

 Let's assume that the size of the hash table is 7, and we will use the formula
(k1 % m) where m is the size of the hash table.
 The following table shows the hash values that will be generated.

Key Hash Algorithm Hash Value


(k1 % m)
3 3%7 3
2 3%7 2
9 3%7 2
11 3%7 4
7 3%7 0

 As we can see from the above results, the values 2 and 9 have the same
hash value, and we cannot store more than one value at each position.
 The given problem can be solved by either using chaining or probing. The
following sections discuss chaining and probing in detail.
Separate chaining (open hashing)
 Separate chaining is one of the most commonly used collision resolution
techniques.
 It is usually implemented using linked lists.
 In separate chaining, each element of the hash table is a linked list.
 To store an element in the hash table we must insert it into a specific linked
list. If there is any collision (i.e. two different elements have same hash
value) then store both the elements in the same linked list.
Example
Open Addressing

 The open addressing is another technique for collision resolution.


 Unlike chaining, it does not insert elements to some other data-structures.
 It inserts the data into the hash table itself. The size of the hash table should
be larger than the number of keys.
 There are three different popular methods for open addressing techniques.
These methods are −

 Linear Probing
 Quadratic Probing
 Double Hashing
Linear Probing
 It is a Scheme in Computer Programming for resolving collision in hash
tables.
 Suppose a new record R with key k is to be added to the memory table T
but that the memory locations with the hash address H (k). H is already
filled.
 Our natural key to resolve the collision is to crossing R to the first available
location following T (h). We assume that the table T with m location is
circular, so that T [i] comes after T [m].

 Given an ordinary hash function h': U {0, 1...m-1}, the method of linear
probing uses the hash function.
h (k, i) = (h' (k) + i) mod m

Where 'm' is the size of hash table and h' (k) = k mod m. for i=0, 1....m-1.
Example
Quadratic Probing
Example
Double Hashing
Example

You might also like