Graph & Hashing
Graph & Hashing
Algorithms
Parenthesis Checker (Bracket Matching)
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
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
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.
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.
Once the largest disk has been moved, we must move, in the similar
manner, all the smaller disks to pin C.
Algorithm
Adding an
element
Front of queue
New element is
added to the rear
of the queue
Conceptual View of a Queue
Removing an element
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
front rear
0 1 2 3 4 5 6 7 8
12 8 9
front rear
Operations on Queues
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 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
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.
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
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
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
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.
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.
Information Part
Link field or nextpointer field
100
Suppose we want to store list of integer numbers, then the linear linked list
can be represented in memory with the following declarations.
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 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
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
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
18 100
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
18 150
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
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
150
loc ptr
5 201 7 350
150 10 350 19 NULL
150
100
Suppose we want to store list of integer numbers, then the doubly linked list
can be represented in memory with the following declarations.
createEmptyList( )
Begin
head = tail = NULL
End
Traversing a List
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
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
150 2 NULL
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
stack *top;
top
100
700
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.
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
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
Because linked list is a dynamic data structure & node created at the run
time.
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.
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 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
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:
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:
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 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 .
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
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
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
It finds that both 14 and 33 are already in ascending order. For now, 14 is in sorted sub-list.
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.
So we swap them.
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
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
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
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
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
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
createrTree()
Begin
root = NULL
end
Traversing a BST
In order Traversal-
10 , 20 , 30 , 100 , 150 , 200 , 300
}
}
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 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:
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.
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:
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.
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.
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.
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
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
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 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.
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
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
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.
There are two ways to store Graph into the computer's memory.
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.
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.
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 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
for I = 1 to n by 1 do
set color[I] = WHITE
endfor
Call CreateQueue(q);
while(ptr!= NULL) do
if ( color[ptr->info] = WHITE) then
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.
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)
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).
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)
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
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)
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
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
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
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
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.
A collision occurs when the algorithm generates the same hash for more
than one value.
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.
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
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