Algorithm Ebook
Algorithm Ebook
LAKSHMI PUBLICATIONS
Plot No.73, VGP Gokul Nagar, 2nd Main Road (40 Feet Road),
Perumbakkam, Chennai-600 100, Tamil Nadu, INDIA.
Phone: 044-49523977, 98945 98598
E-mail: [email protected]
[email protected]
Website: www.lakshmipublications.com
ALGORITHMS
by
Price: Rs.
ISBN
Published by and copies can be had from:
The audience's goal and purpose are more tightly focused. More effort has been
put into getting more accurate results. Readers will feel more at ease and
comprehend the book's depth thanks to its contents.
A brief introduction and concise captions for the figures and tables are provided
for the content in the book. More emphasis is placed on avoiding grammar mistakes
and using the proper format.
- AUTHORS
ACKNOWLEDGEMENT
We first and foremost express our gratitude to GOD ALMIGHTY for giving us the
We would like to extend our sincere appreciation to everyone who helped out,
discussed things, supplied feedback, enabled us to use their comments, and helped
We also thank our Family members, Friends and other loved ones whose
With great concern for the welfare of engineering students, we would like to
publications and Mrs. P.S. Malathi for providing professional support in all of the
Suggestions and comments for further improvements of this book are most
UNIT I: INTRODUCTION 9
Algorithm analysis: Time and space complexity - Asymptotic Notations and its
properties Best case, Worst case and average case analysis - Recurrence relation:
substitution method - Lower bounds - searching: linear search, binary search and
Interpolation Search, Pattern search: The naïve string-matching algorithm - Rabin-
Karp algorithm - Knuth-Morris-Pratt algorithm. Sorting: Insertion sort - heap sort.
UNIT II
UNIT III
UNIT IV
UNIT V
Analyzing an algorithm has come to mean predicting the resources that the
algorithm requires. Occasionally, resources such as memory,
communication band-width, or computer hardware are of primary concern,
but most often it is computational time that we want to measure.
Generally, by analyzing several candidate algorithms for a problem, we
can identify a most efficient one. Such analysis may indicate more than
one viable candidate.
Before we can analyze an algorithm, have a model of the implementation
technology that implements, including a model for the resources of that
technology and their costs.
Thus, k1 = 7
Hence, the complexity of f (n) can be represented as Ω (g (n))
Example:
f (n) = 2 n 2 + 5 is O( n 2 )
then, 7* f (n) = 7(2 n 2 + 5) = 14 n 2 + 35 is also O( n2).
Similarly, this property satisfies both Θ and Ω notation.
2. Transitive Properties:
If f (n) is O(g(n)) and g(n) is O(h(n)) then f (n) = O(h(n)).
Example:
If f (n) = n, g(n) = n 2 and h(n) = n 3
n is O( n 2) and n 2 is O(n 3) then, n is O(n 3)
Similarly, this property satisfies both Θ and Ω notation.
3. Reflexive Properties:
Reflexive properties are always easy to understand after transitive.
If f (n) is given then f (n) is O(f (n)). Since MAXIMUM VALUE OF f (n) will be
f (n) ITSELF!
Hence x = f (n) and y = O(f (n) tie themselves in reflexive relation always.
Example:
f (n) = n 2 ; O( n 2) i.e O(f (n))
Similarly, this property satisfies both Θ and Ω notation.
4. Symmetric Properties:
If f (n) is Θ(g(n)) then g(n) is Θ(f (n)).
Example:
If(n) = n 2 and g(n) = n 2
then, f (n) = Θ( n 2) and g(n) = Θ(n 2)
This property only satisfies for Θ notation.
Example:
If(n) = n , g(n) = n 2
then n is O(n 2) and n 2 is Ω (n)
This property only satisfies O and Ω notations.
Example:
f (n) = n i.e O(n)
d(n) = n 2 i.e O(n 2)
then f (n) + d(n) = n + n 2 i.e O(n 2)
Output
30 is present at index 2
Time Complexity Analysis: (In Big-O notation)
Best Case: O(1), This will take place if the element to be searched is on
the first index of the given list. So, the number of comparisons, in this
case, is 1.
Average Case: O(n), This will take place if the element to be searched
is on the middle index of the given list.
Worst Case: O(n), This will take place if:
The element to be searched is on the last index
The element to be searched is not present on the list
T (n) = θ (1) if n = 1
n
2T 2 + θ (n) if n > 1
There are four methods for solving Recurrence:
1. Substitution Method
2. Iteration Method
3. Recursion Tree Method
4. Master Method
order as g(n), then we know that asymptotically we cannot do better. Lower Bound
Theory Concept is based upon the calculation of minimum time that is required to
execute an algorithm is known as a lower bound theory or Base Bound Theory.
Lower Bound Theory uses a number of methods/techniques to find out the lower
bound
If f (n) is the time for some algorithm, then we write f (n) = Ω(g(n)) to mean
that g(n) is the lower bound of f (n). This equation can be formally written, if there
exists positive constants c and n 0 such that |f (n)| >= c|g(n)| for all n > n 0 . In
addition for developing lower bounds within the constant factor, we are more
conscious of the fact to determine more exact bounds whenever this is possible.
Deriving good lower bounds is more difficult than devising efficient algorithms. This
happens because a lower bound states a fact about all possible algorithms for solving
a problem. Generally, we cannot enumerate and analyze all these algorithms, so
lower bound proofs are often hard to obtain.
The proofing techniques that are useful for obtaining lower bounds are:
1. Comparison trees:
Comparison trees are the computational model useful for determining lower
bounds for sorting and searching problems.
Application:
The lower bound is necessary for any algorithm as after we have completed it, we
can compare it with the actual complexity of the algorithm. If the complexity and the
1.12 Algorithms
order of the algorithm are the same, then we can declare the algorithm optimal. One
of the best examples of optimal algorithms is merge sort. The upper bound should
match the lower bound, that is, L(n) = U(n). The easiest method to find the lower
bound is the trivial lower bound method. If we can easily observe the lower bounds
on the basis of the number of inputs taken and outputs produced, then it is known as
the trivial lower bound method. For example, multiplication of n n matrix.
5. Ordered Searching
It is a type of searching in which the list is already sorted.
Explanation
In linear search, we compare the key with the first element if it does not match we
compare it with the second element, and so on till we check against the nth element.
Else we will end up with a failure. Else, Linear search is a sequential searching
algorithm where we start from one end and check every element of the list until the
desired element is found. It is the simplest searching algorithm.
Fig. 1.4.
Let the element to be searched is K = 41
Now, start from the first element and compare K with each element of the array.
Fig. 1.5.
Introduction 1.13
The value of K, i.e., 41, is not matched with the first element of the array. So,
move to the next element. And follow the same process until the respective element
is found.
Fig. 1.6.
Now, the element to be searched is found. So algorithm will return the index of
the element
1.3.2. ALGORITHM
1. Linear_Search(a, n, val) // 'a' is the given array, 'n' is the size of given
array, 'val' is the value to search
2. Step 1: set pos = –1
3. Step 2: set i = 1
4. Step 3: repeat step 4 while i n
5. Step 4: if a[i] == val
1.14 Algorithms
6. set pos = i
7. print pos
8. go to step 6
9. [end of if]
10. set ii = i + 1
11. [end of loop]
12. Step 5: if pos = –1
13. print "value is not present in the array "
14. [end of if]
15. Step 6: exit
Example:
#include <stdio.h>
int linearSearch(int a[], int n, int val) {
// Going through array sequencially
for (int i = 0; i < n; i++)
{
if (a[i] == val)
return i+1;
}
return -1;
}
int main() {
int a[] = {70, 40, 30, 11, 57, 41, 25, 14, 52}; // given array
int val = 41; // value to be searched
int n = sizeof(a) / sizeof(a[0]); // size of array
int res = linearSearch(a, n, val); // Store result
printf("The elements of the array are - ");
for (int i = 0; i < n; i++)
printf("%d ", a[i]);
Introduction 1.15
Output
The elements of the array are – 70 40 30 11 57 41 25 14 52
Element to be searched is – 41
Element is present at 6 position of array
Explanation
In binary search, we check the middle element against the key, if it is greater we
search the first half else we check the second half and repeat the same process. The
diagram below there is an illustration of binary search in an array consisting of 4
elements
Fig. 1.7.
1.16 Algorithms
If a match occurs, then the index of the item is returned. To split the list into two
parts, we use the following method −
mid = Lo + ((Hi - Lo) / (A[Hi] – A[Lo])) * (X – A[Lo])
where −
A = list
Lo = Lowest index of the list
Hi = Highest index of the list
A[n] = Value stored at index n in the list
If the middle item is greater than the item, then the probe position is again
calculated in the sub-array to the right of the middle item. Otherwise, the item is
searched in the subarray to the left of the middle item. This process continues on the
sub-array as well until the size of subarray reduces to zero.
Runtime complexity of interpolation search algorithm is Ο(log (log n)) as
compared to Ο(log n) of BST in favorable situations.
Algorithm
As it is an improvisation of the existing BST algorithm, we are mentioning the
steps to search the 'target' data value index, using position probing −
Step 1 − Start searching data from middle of the list.
Step 2 − If it is a match, return the index of the item, and exit.
Step 3 − If it is not a match, probe position.
Step 4 − Divide the list using probing formula and find the new midle.
Step 5 − If data is greater than middle, search in higher sub-list.
Step 6 − If data is smaller than middle, search in lower sub-list.
Step 7 − Repeat until match.
1.18 Algorithms
The naïve approach tests all the possible placement of Pattern P [1 m] relative
to text T [1 .n]. We try shift s = 0, 1 n – m, successively and for each shift s.
Compare T [s + 1 s + m] to P [1 .m].
The naïve algorithm finds all valid shifts using a loop that checks the condition P
[1 m] = T [s + 1 s + m] for each of the n – m +1 possible value of s.
NAIVE-STRING-MATCHER (T, P)
1 n length [T]
2. m length [P]
3. for s 0 to n – m
4. do if P [1 .m] = T [s + 1 s + m]
5. then print "Pattern occurs with shift" s
Example:
Suppose T = 1011101110
P = 111
Find all the Valid Shift
1.20 Algorithms
The Rabin-Karp string matching algorithm calculates a hash value for the pattern,
as well as for each M-character subsequences of text to be compared. If the hash
values are unequal, the algorithm will determine the hash value for next M-character
sequence. If the hash values are equal, the algorithm will analyze the pattern and the
M-character sequence. In this way, there is only one comparison per text
subsequence, and character matching is only required when the hash values match.
RABIN-KARP-MATCHER (T, P, d, q)
1. n length [T]
2. m length [P]
3. h dm- – 1 mod q
4. p 0
Introduction 1.21
5. t0 0
6. for i 1 to m
7. do p (d p + P[i]) mod q
8. t 0 (d t 0+T [i]) mod q
9. for s 0 to n – m
10. do if p = ts
11. then if P [1 m] = T [s + 1 s + m]
12. then "Pattern occurs with shift" s
13. If s < n – m
14. then t s +1 (d (ts -T [s + 1]h)+T [s + m + 1])mod q
Example: For string matching, working module q = 11, how many spurious hits
does the Rabin-Karp matcher encounters in Text T = 31415926535.
1. T = 31415926535.......
2. P = 26
3. Here T. Length =11 so Q = 11
4. And P mod Q = 26 mod 11 = 4
5. Now find the exact match of P mod Q...
1.22 Algorithms
Knuth-Morris and Pratt introduce a linear time algorithm for the string matching
problem. A matching time of O (n) is achieved by avoiding comparison with an
element of 'S' that have previously been involved in comparison with some element
of the pattern 'p' to be matched. i.e., backtracking on the string 'S' never occurs
Introduction 1.23
Solution:
Initially: m = length [p] = 7
1.24 Algorithms
Π [1] = 0
k =0
Step 1: q = 2, k = 0
[2] = 0
q 1 2 3 4 5 6 7
p a b a b a c a
0 0
Step 2: q = 3, k = 0
[3] = 1
q 1 2 3 4 5 6 7
p a b a b a c a
0 0 1
Step 3: q = 4, k = 1
[4] = 2
q 1 2 3 4 5 6 7
p a b a b a c A
0 0 1 2
Step 4: q = 5, k = 2
[5] = 3
q 1 2 3 4 5 6 7
p a b a b a c a
0 0 1 2 3
Step 5: q = 6, k = 3
[6] = 0
q 1 2 3 4 5 6 7
p a b a b a c a
0 0 1 2 3 0
Introduction 1.25
Step 6: q = 7, k = 1
[7] = 1
q 1 2 3 4 5 6 7
p a b a b a c a
0 0 1 2 3 0 1
KMP-MATCHER (T, P)
1. n length [T]
2. m length [P]
3. Π COMPUTE-PREFIX-FUNCTION (P)
4. q 0 // numbers of characters matched
5. for i 1 to n // scan S from left to right
6. do while q > 0 and P [q + 1] ≠ T [i]
7. do q Π [q] // next character does not match
8. If P [q + 1] = T [i]
9. then q q + 1 // next character matches
10. If q = m // is all of p matched?
11. then print "Pattern occurs with shift" i – m
12. q Π [q]
Let execute the KMP Algorithm to find whether 'P' occurs in 'T.'
For 'p' the prefix function, ? was computed previously and is as follows:
q 1 2 3 4 5 6 7
p a b A b a c a
0 0 1 2 3 0 1
Solution:
Initially: n = size of T = 15
m = size of P = 7
Step 1: i = 1 q = 0
P(1) does not match will T[1] ‘p’ will be shifted one position to the right
Step 2: i = 2, q = 0
Step 3: i = 3, q = 1
Step 4: i = 4, q = 0
Step 5: i = 5, q = 0
Step 6: i = 6, q = 1
Step 7: i = 7, q = 2
Step 8: i = 8, q = 3
Step 9: i = 9, q = 4
Step 13: i = 3, q = 6
Pattern 'P' has been found to complexity occur in a string 'T.' The total number of
shifts that took place for the match to be found is i – m = 13 – 7 = 6 shifts.
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.
This process goes on until all the unsorted values are covered in a sorted sub-list.
Algorithm
Step 1 − If it is the first element, it is already sorted. return 1;
Step 2 − Pick next element
Step 3 − Compare with all elements in the sorted sub-list
Step 4 − Shift all the elements in the sorted sub-list that is greater than the
value to be sorted
Step 5 − Insert the value
Step 6 − Repeat until list is sorted
Pseudocode
procedure insertionSort( A : array of items )
int holePosition
int valueToInsert
for i = 1 to length(A) inclusive do:
/* select value to be inserted */
valueToInsert = A[i]
holePosition = i
/*locate hole position for the element to be inserted */
end while
Fig. 1.8.
First, we have to construct a heap from the given array and convert it into max
heap.
Fig. 1.9.
After converting the given heap into max heap, the array elements are -
Fig. 1.10.
Next, we have to delete the root element (89) from the max heap. To delete this
node, we have to swap it with the last node, i.e. (11). After deleting the root element,
we again have to heapify it to convert it into max heap.
Fig. 1.11.
1.34 Algorithms
After swapping the array element 89 with 11, and converting the heap into max-
heap, the elements of array are -
Fig. 1.12.
In the next step, again, we have to delete the root element (81) from the max heap.
To delete this node, we have to swap it with the last node, i.e. (54). After deleting the
root element, we again have to heapify it to convert it into max heap.
Fig. 1.13.
After swapping the array element 81 with 54 and converting the heap into max-
heap, the elements of array are -
Fig. 1.14.
In the next step, we have to delete the root element (76) from the max heap again.
To delete this node, we have to swap it with the last node, i.e. (9). After deleting the
root element, we again have to heapify it to convert it into max heap.
Fig. 1.15.
Introduction 1.35
After swapping the array element 76 with 9 and converting the heap into max-
heap, the elements of array are -
Fig. 1.16.
In the next step, again we have to delete the root element (54) from the max heap.
To delete this node, we have to swap it with the last node, i.e. (14). After deleting the
root element, we again have to heapify it to convert it into max heap.
Fig. 1.17.
After swapping the array element 54 with 14 and converting the heap into max-
heap, the elements of array are -
Fig. 1.18.
In the next step, again we have to delete the root element (22) from the max heap.
To delete this node, we have to swap it with the last node, i.e. (11). After deleting the
root element, we again have to heapify it to convert it into max heap.
Fig. 1.19.
1.36 Algorithms
After swapping the array element 22 with 11 and converting the heap into max-
heap, the elements of array are -
Fig. 1.20.
In the next step, again we have to delete the root element (14) from the max heap.
To delete this node, we have to swap it with the last node, i.e. (9). After deleting the
root element, we again have to heapify it to convert it into max heap.
Fig. 1.21.
After swapping the array element 14 with 9 and converting the heap into max-
heap, the elements of array are -
Fig. 1.22.
In the next step, again we have to delete the root element (11) from the max heap.
To delete this node, we have to swap it with the last node, i.e. (9). After deleting the
root element, we again have to heapify it to convert it into max heap.
Fig. 1.23.
After swapping the array element 11 with 9, the elements of array are -
Fig. 1.24.
Now, heap has only one element left. After deleting it, heap will be empty.
Introduction 1.37
Fig. 1.25.
After completion of sorting, the array elements are -
Fig. 1.26.
Now, the array is completely sorted.
Algorithm
heapify(array, size)
Input − An array of data, and the total number in the array
Output − The max heap using an array element
Begin
for i := 1 to size do
node := i
par := floor (node / 2)
while par >= 1 do
if array[par] < array[node] then
swap array[par] with array[node]
node := par
par := floor (node / 2)
done
done
End
heapSort(array, size)
Input: An array of data, and the total number in the array
Output −nbsp;sorted array
Begin
for i := n to 1 decrease by 1 do
heapify(array, i)
1.38 Algorithms
Example
#include<iostream>
using namespace std;
void display(int *array, int size) {
for(int i = 1; i<=size; i++)
cout << array[i] << " ";
cout << endl;
}
}
Introduction 1.39
int i;
int main() {
int n;
cin >> n;
display(arr, n);
heapSort(arr, n);
display(arr, n);
}
1.40 Algorithms
Output
Enter the number of elements: 6
Enter elements:
30 8 99 11 24 39
Array before Sorting: 30 8 99 11 24 39
Array after Sorting: 8 11 24 30 39 99
9. What is Sorting?
A Sorting Algorithm is used to rearrange a given array or list of elements
according to a comparison operator on the elements. The comparison operator is
used to decide the new order of elements in the respective data structure.
10. What is 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 'insert'ed in this sorted sub-
list, has to find its appropriate place and then it has to be inserted there. Hence
the name, insertion sort.
11. What is Heap Sort Algorithm.
Heap sort processes the elements by creating the min-heap or max-heap using
the elements of the given array. Min-heap or max-heap represents the ordering
of array in which the root element represents the minimum or maximum element
of the array.
*******************
UNIT II
GRAPH ALGORITHMS
Graph algorithms: Representations of graphs - Graph traversal: DFS – BFS -
applications - Connectivity, strong connectivity, bi-connectivity - Minimum
spanning tree: Kruskal’s and Prim’s algorithm- Shortest path: Bellman-Ford
algorithm - Dijkstra’s algorithm - Floyd-Warshall algorithm Network flow: Flow
networks - Ford-Fulkerson method – Matching: Maximum bipartite matching
Sequential Representation
In sequential representation, there is a use of an adjacency matrix to represent the
mapping between vertices and edges of the graph. We can use an adjacency matrix to
2.2 Algorithms
represent the undirected graph, directed graph, weighted directed graph, and
weighted undirected graph.
If adj[i][j] = w, it means that there is an edge exists from vertex i to vertex j with
weight w.
An entry A i j in the adjacency matrix representation of an undirected graph G will
be 1 if an edge exists between Vi and V j . If an Undirected Graph G consists of n
vertices, then the adjacency matrix for that graph is n n, and the matrix A = [ai j ]
can be defined as -
ai j = 1 {if there is a path exists from Vi to V j }
ai j = 0 {Otherwise}
It means that, in an adjacency matrix, 0 represents that there is no association
exists between the nodes, whereas 1 represents the existence of a path between two
edges.
If there is no self-loop present in the graph, it means that the diagonal entries of
the adjacency matrix will be 0.
Now, let's see the adjacency matrix representation of an undirected graph.
Fig. 2.1.
Fig. 2.2.
Let's see the adjacency list representation of an undirected graph.
In the above figure, we can see that there is a linked list or adjacency list for every
node of the graph. From vertex A, there are paths to vertex B and vertex D. These
nodes are linked to nodes A in the given adjacency list.
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 the last
node of the list.
The sum of the lengths of adjacency lists is equal to twice the number of edges
present in an undirected graph.
Graph traversal means visiting every vertex and edge exactly once in a well-
defined order. While using certain graph algorithms, you must ensure that each
vertex of the graph is visited exactly once. The order in which the vertices are visited
are important and may depend upon the algorithm or question that you are solving.
During a traversal, it is important that you track which vertices have been visited.
The most common way of tracking vertices is to mark them.
can use any random node as the root node) and examines each branch as far as
possible before backtracking.
Fig. 2.3.
When a dead-end occurs in any iteration, the Depth First Search (DFS) method
traverses a network in a deathward motion and uses a stack data structure to
remember to acquire the next vertex to start a search.
Consider the following graph as an example of how to use the dfs algorithm.
Fig. 2.4.
Step 1: Mark vertex A as a visited source node by selecting it as a source
node.
You should push vertex A to the top of the stack.
Fig. 2.5.
Step 2: Any nearby unvisited vertex of vertex A, say B, should be visited.
You should push vertex B to the top of the stack.
Graph Algorithms 2.5
Fig. 2.6.
Step 3: From vertex C and D, visit any adjacent unvisited vertices of vertex B.
Imagine you have chosen vertex C, and you want to make C a visited
vertex.
Vertex C is pushed to the top of the stack.
Fig. 2.7.
Step 4: You can visit any nearby unvisited vertices of vertex C, you need to
select vertex D and designate it as a visited vertex.
Vertex D is pushed to the top of the stack.
Fig. 2.8.
Step 5: Vertex E is the lone unvisited adjacent vertex of vertex D, thus
marking it as visited.
Fig. 2.9.
Vertex E should be pushed to the top of the stack.
2.6 Algorithms
Step 6: Vertex E's nearby vertices, namely vertex C and D have been visited,
pop vertex E from the stack.
Fig. 2.10.
Step 7: Now that all of vertex D's nearby vertices, namely vertex B and C,
have been visited, pop vertex D from the stack.
Fig. 2.11.
Step 8: Similarly, vertex C's adjacent vertices have already been visited;
therefore, pop it from the stack.
Fig. 2.12.
Step 9: There is no more unvisited adjacent vertex of b, thus pop it from the
stack.
Fig. 2.13.
Step 10: All of the nearby vertices of Vertex A, B, and C, have already been
visited, so pop vertex A from the stack as well.
Graph Algorithms 2.7
Fig. 2.14.
Depth_First_Search (v)
color[v] GRAY
time time + 1
d[v] time
for each vertex u adjacent to v
do if color[u] WHITE
π[u] v
Depth_First_Search(u)
color[v] BLACK
time time + 1
f [v] time
2.8 Algorithms
Fig. 2.15.
The distance between the nodes in layer 1 is comparitively lesser than the distance
between the nodes in layer 2. Therefore, in BFS, you must traverse all the nodes in
layer 1 before you move to the nodes in layer 2.
To make this process easy, use a queue to store the node and mark it as 'visited'
until all its neighbours (vertices that are directly connected to it) are marked. The
queue follows the First In First Out (FIFO) queuing method, and therefore, the
neigbors of the node will be visited in the order in which they were inserted in the
node i.e. the node that was inserted first will be visited first, and so on.
TRAVERSING PROCESS
Fig. 2.16.
The traversing will start from the source node and push s in queue. s will be
marked as 'visited'.
First Iteration
s will be popped from the queue
Neighbors of s i.e. 1 and 2 will be traversed
2.10 Algorithms
Fig. 2.17.
1 and 2, which have not been
traversed earlier, are traversed. They will be:
o Pushed in the queue
o 1 and 2 will be marked as visited
Second iteration
1 is popped from the queue
Neighbors of 1 i.e. s and 3 are traversed
s is ignored because it is marked as 'visited'
3, which has not been traversed earlier, is traversed. It is:
o Pushed in the queue
o Marked as visited
Third iteration
2 is popped from the queue
Neighbors of 2 i.e. s, 3, and 4 are traversed
3 and s are ignored because they are marked as 'visited'
4, which has not been traversed earlier, is traversed. It is:
o Pushed in the queue
o Marked as visited
Graph Algorithms 2.11
Fourth iteration
3 is popped from the queue
Neighbors of 3 i.e. 1, 2, and 5 are traversed
1 and 2 are ignored because they are marked as 'visited'
5, which has not been traversed earlier, is traversed. It is:
o Pushed in the queue
o Marked as visited
Fifth iteration
4 will be popped from the queue
Neighbors of 4 i.e. 2 is traversed
2 is ignored because it is already marked as 'visited'
Sixth iteration
5 is popped from the queue
Neighbors of 5 i.e. 3 is traversed
3 is ignored because it is already marked as 'visited'
The queue is empty and it comes out of the loop. All the nodes have been
traversed by using BFS.
If all the edges in a graph are of the same weight, then BFS can also be used to
find the minimum distance between the nodes in a graph.
Q{}
ENQUEUE (Q,s)
While Q is non-empty
do v DEQUEUE (Q)
for each u adjacent to v
do if color[u] WHITE
then color[u] GREY
d[u] d[v] + 1
π[u] v
ENQUEUE (Q, u)
color[v] BLACK
the vertex to stack. In the above graph, if we start DFS from vertex 0, we
get vertices in stack as 1, 2, 4, 3, 0.
2. Reverse directions of all arcs to obtain the transpose graph.
3. One by one pop a vertex from S while S is not empty. Let the popped
vertex be ‘v’. Take v as source and do DFS (call DFSUtil(v)). The DFS
starting from v prints strongly connected component of v. In the above
example, we process vertices in order 0, 3, 4, 2, 1 (One by one popped
from stack).
How does this work?
The above algorithm is DFS based. It does DFS two times. DFS of a graph
produces a single tree if all vertices are reachable from the DFS starting point.
Otherwise DFS produces a forest. So DFS of a graph with only one SCC always
produces a tree. The important point to note is DFS may produce a tree or a forest
when there are more than one SCCs depending upon the chosen starting point. For
example, in the above diagram, if we start DFS from vertices 0 or 1 or 2, we get a
tree as output. And if we start from 3 or 4, we get a forest. To find and print all
SCCs, we would want to start DFS from vertex 4 (which is a sink vertex), then move
to 3 which is sink in the remaining set (set excluding 4) and finally any of the
remaining vertices (0, 1, 2).
2.2.5. BICONNECTIVITY
An undirected graph is said to be a biconnected graph, if there are two vertex-
disjoint paths between any two vertices are present. In other words, we can say that
there is a cycle between any two vertices.
vertices are visited by the DFS or not, if not we can say that the graph is not
connected.
Here's the pseudo code:
time = 0
function is Biconnected(vertex, adj[][], low[], disc[], parent[], visited[], V)
disc[vertex]=low[vertex]=time+1
time = time + 1
visited[vertex]=true
child = 0
for i = 0 to V
if adj[vertex][i] == true
if visited[i] == false
child = child + 1
parent[i] = vertex
result = isBiconnected(i, adj, low, disc, visited, V, time)
if result == false
return false
low[vertex] = minimum(low[vertex], low[i])
if parent[vertex] == nil AND child > 1
return false
if parent[vertex] != nil AND low[i] >= disc[vertex]
return false
else if parent[vertex] != i
low[vertex] = minimum(disc[i], low[vertex])
return true
Given an undirected and connected graph G = (V, E), a spanning tree of the
graph G is a tree that spans G (that is, it includes every vertex of G) and is a
subgraph of G (every edge in the tree belongs to G)
2.16 Algorithms
Fig. 2.21.
There are two famous algorithms for finding the Minimum 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.
Graph Algorithms 2.17
Only add edges which doesn't form a cycle , edges which connect only
disconnected components.
Consider following example:
weighted edge i.e., edge with weight 5. Now the other two edges will create cycles so
we will ignore them. In the end, we end up with a minimum spanning tree with total
cost 11 ( = 1 + 2 + 3 + 5).
Time Complexity:
In Kruskal’s algorithm, most time consuming operation is sorting because the
total complexity of the Disjoint-Set operations will be O(ElogV), which is the overall
Time Complexity of the algorithm.
Prim’s Algorithm also use Greedy approach to find the minimum spanning tree.
In Prim’s Algorithm we grow the spanning tree from a starting position. Unlike
an edge in Kruskal's, we add vertex to the growing spanning tree in Prim's.
Algorithm Steps:
Maintain two disjoint sets of vertices. One containing vertices that are in
the growing spanning tree and other that are not in the growing spanning
tree.
Select the cheapest vertex that is connected to the growing spanning tree
and is not in the growing spanning tree and add it into the growing
spanning tree. This can be done using Priority Queues. Insert the vertices,
that are connected to growing spanning tree, into the Priority Queue.
Check for cycles. To do that, mark the nodes which have been already
selected and insert only those nodes in the Priority Queue that are not
marked.
In Prim’s Algorithm, we will start with an arbitrary node (it doesn’t matter which
one) and mark it. In each iteration we will mark a new vertex that is adjacent to the
one that we have already marked. As a greedy algorithm, Prim’s algorithm will select
the cheapest edge and mark the vertex. So we will simply choose the edge with
weight 1. In the next iteration we have three options, edges with weight 2, 3 and 4.
So, we will select the edge with weight 2 and mark the vertex. Now again we have
three options, edges with weight 3, 4 and 5. But we can’t choose edge with weight 3
Graph Algorithms 2.19
as it is creating a cycle. So we will select the edge with weight 4 and we end up with
the minimum spanning tree of total cost 7 ( = 1 + 2 + 4).
Consider the example below:
Time Complexity:
The time complexity of the Prim’s Algorithm is O((V + E)logV) because each
edge is inserted in the priority queue only once and insertion in priority queue take
logarithmic time.
Shortest path algorithms are a family of algorithms designed to solve the shortest
path problem. The shortest path problem is something most people have some
intuitive familiarity with: given two points, A and B, what is the shortest path
between them? In computer science, however, the shortest path problem can take
different forms and so different algorithms are needed to be able to solve them all.
Applications-
Shortest path algorithms have a wide range of applications such as in-
Google Maps
Road Networks
Logistics Research
2.20 Algorithms
Fig. 2.24.
Step 2: Choose a starting vertex and assign infinity path values to all other
vertices.
Fig. 2.25.
Step 3: Visit each edge and relax the path distances if they are inaccurate.
Fig. 2.26.
Fig. 2.27.
Graph Algorithms 2.21
Step 5: Notice how the vertex at the top right corner had its path length
adjusted.
Fig. 2.28.
Step 6: After all the vertices have their path lengths, we check if a negative
cycle is present.
B C D E
0
0 4 2
0 3 2 6 6
0 3 2 1 6
0 3 2 1 6
Fig. 2.29.
2.22 Algorithms
Fig. 2.30.
Let's assume that the vertex 0 is represented by 'x' and the vertex 1 is represented
by 'y'. The distance between the vertices can be calculated by using the below
formula:
1. d(x, y) = d(x) + c(x, y) < d(y)
2. = (0 + 4) < ∞
3. =4<∞
Since 4 < ∞ so we will update d(v) from ∞ to 4.
Therefore, we come to the conclusion that the formula for calculating the distance
between the vertices:
1. {if( d(u) + c(u, v) < d(v))
2. d(v) = d(u) + c(u, v) }
Now we consider vertex 0 same as 'x' and vertex 4 as 'y'.
1. d(x, y) = d(x) + c(x, y) < d(y)
2. = (0 + 8) < ∞
3. =8<∞
Therefore, the value of d(y) is 8. We replace the infinity value of vertices 1 and 4
with the values 4 and 8 respectively. Now, we have found the shortest path from the
Graph Algorithms 2.23
vertex 0 to 1 and 0 to 4. Therefore, vertex 0 is selected. Now, we will compare all the
vertices except the vertex 0. Since vertex 1 has the lowest value, i.e., 4; therefore,
vertex 1 is selected.
Since vertex 1 is selected, so we consider the path from 1 to 2, and 1 to 4. We will
not consider the path from 1 to 0 as the vertex 0 is already selected.
First, we calculate the distance between the vertex 1 and 2. Consider the vertex 1
as 'x', and the vertex 2 as 'y'.
d(x, y) = d(x) + c(x, y) < d(y)
= (4 + 8) < ∞
= 12 < ∞
Since 12 < ∞ so we will update d(2) from ∞ to 12.
Now, we calculate the distance between the vertex 1 and vertex 4. Consider the
vertex 1 as 'x' and the vertex 4 as 'y'.
d(x, y) = d(x) + c(x, y) < d(y)
= (4 + 11) < 8
= 15 < 8
Since 15 is not less than 8, we will not update the value d(4) from 8 to 12.
Till now, two nodes have been selected, i.e., 0 and 1. Now we have to compare
the nodes except the node 0 and 1. The node 4 has the minimum distance, i.e., 8.
Therefore, vertex 4 is selected.
Since vertex 4 is selected, so we will consider all the direct paths from the vertex
4. The direct paths from vertex 4 are 4 to 0, 4 to 1, 4 to 8, and 4 to 5. Since the
vertices 0 and 1 have already been selected so we will not consider the vertices 0 and
1. We will consider only two vertices, i.e., 8 and 5.
First, we consider the vertex 8. First, we calculate the distance between the vertex
4 and 8. Consider the vertex 4 as 'x', and the vertex 8 as 'y'.
d(x, y) = d(x) + c(x, y) < d(y)
= (8 + 7) < ∞
= 15 < ∞
Since 15 is less than the infinity so we update d(8) from infinity to 15.
2.24 Algorithms
Now, we consider the vertex 5. First, we calculate the distance between the vertex
4 and 5. Consider the vertex 4 as 'x', and the vertex 5 as 'y'.
d(x, y) = d(x) + c(x, y) < d(y)
= (8 + 1) < ∞
= 9<∞
Since 5 is less than the infinity, we update d(5) from infinity to 9.
Till now, three nodes have been selected, i.e., 0, 1, and 4. Now we have to
compare the nodes except the nodes 0, 1 and 4. The node 5 has the minimum value,
i.e., 9. Therefore, vertex 5 is selected.
Since the vertex 5 is selected, so we will consider all the direct paths from vertex
5. The direct paths from vertex 5 are 5 to 8, and 5 to 6.
First, we consider the vertex 8. First, we calculate the distance between the vertex
5 and 8. Consider the vertex 5 as 'x', and the vertex 8 as 'y'.
d(x, y) = d(x) + c(x, y) < d(y)
= (9 + 15) < 15
= 24 < 15
Since 24 is not less than 15 so we will not update the value d(8) from 15 to 24.
Now, we consider the vertex 6. First, we calculate the distance between the vertex
5 and 6. Consider the vertex 5 as 'x', and the vertex 6 as 'y'.
d(x, y) = d(x) + c(x, y) < d(y)
= (9 + 2) < ∞
= 11 < ∞
Since 11 is less than infinity, we update d(6) from infinity to 11.
Till now, nodes 0, 1, 4 and 5 have been selected. We will compare the nodes
except the selected nodes. The node 6 has the lowest value as compared to other
nodes. Therefore, vertex 6 is selected.
Since vertex 6 is selected, we consider all the direct paths from vertex 6. The
direct paths from vertex 6 are 6 to 2, 6 to 3, and 6 to 7.
First, we consider the vertex 2. Consider the vertex 6 as 'x', and the vertex 2 as 'y'.
d(x, y) = d(x) + c(x, y) < d(y)
Graph Algorithms 2.25
= (11 + 4) < 12
= 15 < 12
Since 15 is not less than 12, we will not update d(2) from 12 to 15
Now we consider the vertex 3. Consider the vertex 6 as 'x', and the vertex 3 as 'y'.
d(x, y) = d(x) + c(x, y) < d(y)
= (11 + 14) < ∞
= 25 < ∞
Since 25 is less than ∞, so we will update d(3) from ∞ to 25.
Now we consider the vertex 7. Consider the vertex 6 as 'x', and the vertex 7 as 'y'.
d(x, y) = d(x) + c(x, y) < d(y)
= (11 + 10) < ∞
= 22 < ∞
Since 22 is less than ∞ so, we will update d(7) from ∞ to 22.
Till now, nodes 0, 1, 4, 5, and 6 have been selected. Now we have to compare all
the unvisited nodes, i.e., 2, 3, 7, and 8. Since node 2 has the minimum value, i.e., 12
among all the other unvisited nodes. Therefore, node 2 is selected.
Since node 2 is selected, so we consider all the direct paths from node 2. The
direct paths from node 2 are 2 to 8, 2 to 6, and 2 to 3.
First, we consider the vertex 8. Consider the vertex 2 as 'x' and 8 as 'y'.
d(x, y) = d(x) + c(x, y) < d(y)
= (12 + 2) < 15
= 14 < 15
Since 14 is less than 15, we will update d(8) from 15 to 14.
Now, we consider the vertex 6. Consider the vertex 2 as 'x' and 6 as 'y'.
d(x, y) = d(x) + c(x, y) < d(y)
= (12 + 4) < 11
= 16 < 11
Since 16 is not less than 11 so we will not update d(6) from 11 to 16.
2.26 Algorithms
Now, we consider the vertex 3. Consider the vertex 2 as 'x' and 3 as 'y'.
d(x, y) = d(x) + c(x, y) < d(y)
= (12 + 7) < 25
= 19 < 25
Since 19 is less than 25, we will update d(3) from 25 to 19.
Till now, nodes 0, 1, 2, 4, 5, and 6 have been selected. We compare all the
unvisited nodes, i.e., 3, 7, and 8. Among nodes 3, 7, and 8, node 8 has the minimum
value. The nodes which are directly connected to node 8 are 2, 4, and 5. Since all the
directly connected nodes are selected so we will not consider any node for the
updation.
The unvisited nodes are 3 and 7. Among the nodes 3 and 7, node 3 has the
minimum value, i.e., 19. Therefore, the node 3 is selected. The nodes which are
directly connected to the node 3 are 2, 6, and 7. Since the nodes 2 and 6 have been
selected so we will consider these two nodes.
Now, we consider the vertex 7. Consider the vertex 3 as 'x' and 7 as 'y'.
d(x, y) = d(x) + c(x, y) < d(y)
= (19 + 9) < 2121
= 28 < 21
Since 28 is not less than 21, so we will not update d(7) from 28 to 21.
Time Complexity-
Floyd Warshall Algorithm consists of three loops over all the nodes.
The inner most loop consists of only constant complexity operations.
Hence, the asymptotic complexity of Floyd Warshall algorithm is O(n3).
Here, n is the number of nodes in the given graph.
Example problem
Consider the following directed weighted graph-
Fig. 2.31.
Using Floyd Warshall Algorithm, find the shortest path distance between every
pair of vertices.
Solution:
Step-01:
Remove all the self loops and parallel edges (keeping the lowest weight edge)
from the graph.
In the given graph, there are neither self edges nor parallel edges.
Step-02:
Write the initial distance matrix.
It represents the distance between every pair of vertices in the form of
given weights.
For diagonal elements (representing self-loops), distance value = 0.
For vertices having a direct edge between them, distance value = weight of
that edge.
For vertices having no direct edge between them, distance value = ∞.
2.28 Algorithms
Step-03:
Using Floyd Warshall Algorithm, write the following 4 matrices-
Graph Algorithms 2.29
The last matrix D4 represents the shortest path distance between every pair of
vertices.
Fig. 2.32.
Any flow network should satisfy all the underlying conditions --
For all the vertices (except the source and the sink vertex), input flow must
be equal to output flow.
For any given edge(E_iEi) in the flow network, 0\leq flow(E_i)\leq
capacity(E_i) ≤ flow(Ei) ≤ capacity(Ei) must hold, we can not send more
flow through an edge than its capacity.
Total outflow from the source vertex must be equal to total inflow to the
sink vertex.
2.30 Algorithms
Fig. 2.33.
The maximum possible flow in the above graph is 23.
Fig. 2.34.
A Bipartite Graph is a graph whose vertices can be divided into two independent
sets L and R such that every edge (u, v) either connect a vertex from L to R or a
vertex from R to L. In other words, for every edge (u, v) either u ∈ L and v ∈ L. We
can also say that no edge exists that connect vertices of the same set.
2.32 Algorithms
Fig. 2.35.
Matching is a Bipartite Graph is a set of edges chosen in such a way that no two
edges share an endpoint. Given an undirected Graph G = (V, E), a Matching is a
subset of edge M ⊆ E such that for all vertices v ∈ V, at most one edge of M is
incident on v.
A Maximum matching is a matching of maximum cardinality, that is, a matching
M such that for any matching M', we have |M| > |M' |.
Fig. 2.36. (a)Matching with Cardinally 2 (b) Matching with Cardinality 3 , (c)The
corresponding flow network G′ with a maximum flow shown . Each edge has until
capacity. The shaded edges from L to R correspond to those in the maximum marching
from (b)
exploring the neighbour nodes (nodes which are directly connected to source
node). You must then move towards the next-level neighbour nodes.
5. What is Strongly Connected Components?
A strongly connected component is the portion of a directed graph in which
there is a path from each vertex to another vertex. It is applicable only on a
directed graph.
6. What is BICONNECTIVITY.
An undirected graph is said to be a biconnected graph, if there are two vertex-
disjoint paths between any two vertices are present. In other words, we can say
that there is a cycle between any two vertices
7. What is Minimum spanning tree?
Minimum spanning tree is the spanning tree where the cost is minimum
among all the spanning trees. There also can be many minimum spanning trees.
8. What is Kruskal’s algorithm?
Kruskal’s Algorithm builds the spanning tree by adding edges one by one into
a growing spanning tree. Kruskal's algorithm follows greedy approach as in each
iteration it finds an edge which has least weight and add it to the growing
spanning tree.
9. What is Prim’s Algorithm
Prim’s Algorithm also use Greedy approach to find the minimum spanning
tree. In Prim’s Algorithm we grow the spanning tree from a starting position.
Unlike an edge in Kruskal's, we add vertex to the growing spanning tree in
Prim's.
10. What is Bellman Ford algorithm?
Bellman Ford algorithm works by overestimating the length of the path from
the starting vertex to all other vertices. Then it iteratively relaxes those estimates
by finding new paths that are shorter than the previously overestimated paths.
11. What is Dijkstra algorithm?
Dijkstra algorithm is a single-source shortest path algorithm. Here, single-
source means that only one source is given, and we have to find the shortest path
from the source to all the nodes.
12. What is Floyd Warshall Algorithm?
Floyd Warshall Algorithm is a famous algorithm.
Graph Algorithms 2.35
*******************
UNIT III
ALGORITHM DESIGN
TECHNIQUES
Divide and Conquer methodology: Finding maximum and minimum - Merge sort
- Quick sort Dynamic programming: Elements of dynamic programming - Matrix-
chain multiplication - Multi stage graph - Optimal Binary Search Trees. Greedy
Technique: Elements of the greedy strategy - Activity-selection problem - Optimal
Merge pattern - Huffman Trees.
Divide and conquer algorithm works on top-down approach and is preferred for
large problems. As the name says divide and conquer, it follows following steps:
Step 1: Divide the problem into several subproblems.
3.2 Algorithms
Algorithm Design Techniques 3.3
Analysis
Let T(n) be the number of comparisons made by Max − Min(x, y), where the
number of elements n = y – x + 1.
If T(n) represents the numbers, then the recurrence relation can be represented as
So,
n n 3n
T(n) = 2.T 2 + 2 = 2. 2.T 4 + 2 + 2 = 2 – 2
Compared to Naïve method, in divide and conquer approach, the number of
comparisons is less. However, using the asymptotic notation both of the approaches
are represented by O(n).
Algorithm:
step 1: start
step 2: declare array and left, right, mid variable
step 3: perform merge function.
if left > right
return
mid= (left+right)/2
mergesort(array, left, mid)
mergesort(array, mid+1, right)
merge(array, left, mid, right)
step 4: Stop
Follow the steps below to solve the problem:
MergeSort(arr[], l, r)
If r > l
Find the middle point to divide the array into two halves:
middle m = l + (r – l) / 2
Call mergeSort for first half:
Call mergeSort(arr, l, m)
Call mergeSort for second half:
Algorithm Design Techniques 3.5
Call mergeSort(arr, m + 1, r)
Merge the two halves sorted in steps 2 and 3:
Call merge(arr, l, m, r)
The following figure illustrates the dividing (splitting) procedure.
Fig. 3.1.
3.6 Algorithms
Algorithm:
5 o PARTITION (A, m, n)
6 QUICKSORT (A, m, o - 1)
7 QUICKSORT (A, o + 1, n)
Stopping
Method Name Equation Complexities
Condition
[Worst Case]
Example
Let's find the fibonacci sequence upto 5th term. A fibonacci series is the sequence
of numbers in which each number is the sum of the two preceding ones. For
example, 0, 1, 1, 2, 3 . Here, each number is the sum of the two preceding numbers.
Example: We are given the sequence {4, 10, 3, 12, 20, and 7}. The matrices have
size 4 10, 10 3, 3 12, 12 20, 20 7. We need to compute M [i, j], 0 ≤ i, j ≤
5. We know M [i, i] = 0 for all i.
Fig. 3.3.
Let us proceed with working away from the diagonal. We compute the optimal
solution for the product of 2 matrices.
Fig. 3.4.
Here P0 to P5 are Position and M1 to M5 are matrix of size (p i to p i – 1 )
= 4 10 3 = 120
2. m (2, 3) = m 2 m3
= 10 3 3 12
= 10 3 12 = 360
3. m (3, 4) = m3 m4
= 3 12 12 20
= 3 12 20 = 720
4. m (4,5) = m4 m5
= 12 20 20 7
= 12 20 7 = 1680
Fig. 3.5.
We initialize the diagonal element with equal i, j value with '0'.
After that second diagonal is sorted out and we get all the values
corresponded to it
Now the third diagonal will be solved out in the same way.
M [1, 3] = 264
As Comparing both output 264 is minimum in both cases so we insert 264 in table
and ( M1 M2) + M3 this combination is chosen for the output making.
M [2, 4] = M2 M3 M4
1. There are two cases by which we can solve this multiplication: (M2 M3)
+ M4, M2 + (M3 M4)
2. After solving both cases we choose the case in which minimum output is
there.
M[2,3] + M[4,4] + p 1 p 3 p 4 = 360 + 0 + 10.12.20 = 2760
M[2, 4] = min M[2,2] + M[3,4] + p p p = 0 + 720 + 10.3.20 = 1320
1 2 4
M [2, 4] = 1320
As Comparing both output 1320 is minimum in both cases so we insert 1320 in
table and M2 + (M3 M4) this combination is chosen for the output making.
M [3, 5] = M3 M4 M5
1. There are two cases by which we can solve this multiplication: (M3 M4)
+ M5, M3+ (M4 M5)
2. After solving both cases we choose the case in which minimum output is
there.
M[3,4] + M[5,5] + p 2 p 4 p 5 = 720 + 0 + 3.20.7 = 1140
M[3, 5] = min M[3,3] + M[4,5] + p p p = 0 + 1680 + 3.12.7 = 1932
2 3 5
M [3, 5] = 1140
As Comparing both output 1140 is minimum in both cases so we insert 1140 in
table and (M3 M4) + M5 this combination is chosen for the output making.
Fig. 3.6.
3.12 Algorithms
M [1, 4] = 1080
As comparing the output of different cases then '1080' is minimum output, so we
insert 1080 in the table and (M1 M2) (M3 M4) combination is taken out in
output making,
M [2, 5] = M2 M3 M4 M5
There are three cases by which we can solve this multiplication:
1. (M2 M3 M4) M5
2. M2 (M3 M4 M5)
3. (M2 M3) (M4 M5)
After solving these cases we choose the case in which minimum output is there
M[2,4] + M[5,5] + p 1 p 4 p 5 = 1320 + 0 + 10.20.7 = 2720
M[2, 5] = min M[2,3] + M[4,5] + p 1 p 3 p 5 = 360 + 1680 + 10.12.7 = 2880
M[2,2] + M[3,5] + p 1 p 2 p 5 = 0 + 1140 + 10.3.7 = 1350
M [2, 5] = 1350
Fig. 3.7.
Algorithm Design Techniques 3.13
M [1, 5] = M1 M2 M3 M4 M5
2. M1 (M2 M3 M4 M5)
3. (M1 M2 M3) M4 M5
4. M1 M2 (M3 M4 M5)
After solving these cases we choose the case in which minimum output is there
M[1,3]
M[1,4] + M[5,5] + p 0 p 4 p 5 = 1080 + 0 + 4.20.7 = 1544
+ M[4,5] + p 0 p 3 p 5 = 264 + 1680 + 4.12.7 = 2016
M[1, 5] = min M[1,2] + M[3,5] + p p p = 120 + 1140 + 4.3.7 = 1344
M[1,1] + M[2,5] + p 0 p 1 p 5 = 0 + 1350 + 4.10.7 = 1630
0 2 5
M [1, 5] = 1344
Fig. 3.8.
3.14 Algorithms
Step 3: Computing Optimal Costs: let us assume that matrix Ai has dimension
pi-1x pi for i = 1, 2, 3....n. The input is a sequence (p 0, p 1, p n) where length [p] = n
+ 1. The procedure uses an auxiliary table m [1....n, 1.....n] for storing m [i, j] costs
an auxiliary table s [1 n, 1 .n] that record which index of k achieved the
optimal costs in computing m [i, j].
The algorithm first computes m [i, j] 0 for i = 1, 2, 3 .n , the minimum costs
for the chain of length 1.
Example
Find minimum path cost between vertex s and t for following multistage
graph using dynamic programming.
Algorithm Design Techniques 3.15
Fig. 3.9.
Solution to multistage graph using dynamic programming is constructed as,
Cost[j] = min{c [ j , r] + cost[r]}
Here, number of stages k = 5, number of vertices n = 12, source s = 1 and target t =
12
Initialization:
Cost[n] = 0 Cost[12] = 0.
p[1] = s p[1] = 1
p[k] = t p[5] = 12.
r = t = 12
Stage 4:
Fig. 3.10.
Stage 3:
Fig. 3.11.
Stage 2:
Fig. 3.12.
Stage 1:
p[10] = 12
Fig. 3.13.
Minimum cost path is : 1 – 2 – 7 – 10 – 12
Fig. 3.14.
Cost of the path is : 9 + 2 + 3 + 2 = 16
In binary search tree, the nodes in the left subtree have lesser value than the root
node and the nodes in the right subtree have greater value than the root node.
We know the key values of each node in the tree, and we also know the
frequencies of each node in terms of searching means how much time is required to
search a node. The frequency and key-value determine the overall cost of searching a
node. The cost of searching is a very important factor in various applications. The
overall cost of searching a node should be less. The time required to search a node in
BST is more than the balanced binary search tree as a balanced binary search tree
contains a lesser number of levels than the BST. There is one way that can reduce the
cost of a binary search tree is known as an optimal binary search tree.
Example:
If the keys are 10, 20, 30, 40, 50, 60, 70
Algorithm Design Techniques 3.19
Fig. 3.15.
In the above tree, all the nodes on the left subtree are smaller than the value of the
root node, and all the nodes on the right subtree are larger than the value of the root
node. The maximum time required to search a node is equal to the minimum height
of the tree, equal to log n.
Now we will see how many binary search trees can be made from the given
number of keys.
For example: 10, 20, 30 are the keys, and the following are the binary search trees
that can be made out from these keys.
The Formula for calculating the number of trees:
2n
Cn
n +1
When we use the above formula, then it is found that total 5 number of trees can
be created.
The cost required for searching an element depends on the comparisons to be
made to search an element. Now, we will calculate the average cost of time of the
above binary search trees.
Fig. 3.16.
3.20 Algorithms
In the above tree, total number of 3 comparisons can be made. The average
number of comparisons can be made as:
1+2+3
Average number of comparisons = =2
3
Fig. 3.17.
In the above tree, the average number of comparisons that can be made as:
1 + 2 +3
Average number of comparisons = =2
3
Fig. 3.18.
In the above tree, the average number of comparisons that can be made as:
1+2+2
Average number of comparisons = = 5/3
3
Fig. 3.19.
In the above tree, the total number of comparisons can be made as 3. Therefore,
the average number of comparisons that can be made as:
1+2+3
Average number of comparisons = =2
3
Algorithm Design Techniques 3.21
Fig. 3.20.
In the above tree, the total number of comparisons can be made as 3. Therefore,
the average number of comparisons that can be made as:
1 +2 + 3
Average number of comparisons = =2
3
In the third case, the number of comparisons is less because the height of the tree
is less, so it's a balanced binary search tree.
Let's assume that frequencies associated with the keys 10, 20, 30 are 3, 2, 5.
The above trees have different frequencies. The tree with the lowest frequency
would be considered the optimal binary search tree. The tree with the frequency 17 is
the lowest, so it would be considered as the optimal binary search tree.
Algorithm
optCostBst(keys, freq, n)
Input: Keys to insert in BST, the frequency for each key, number of keys.
Output: Minimum cost to make optimal BST.
Begin
define cost matrix of size n x n
for i in range 0 to n-1, do
cost[i, i] := freq[i]
done
Among all the algorithmic approaches, the simplest and straightforward approach
is the Greedy method. In this approach, the decision is taken on the basis of current
available information without worrying about the effect of the current decision in
future.
Greedy algorithms build a solution part by part, choosing the next part in such a
way, that it gives an immediate benefit. This approach never reconsiders the choices
taken previously. This approach is mainly used to solve optimization problems.
Greedy method is easy to implement and quite efficient in most of the cases. Hence,
we can say that Greedy algorithm is an algorithmic paradigm based on heuristic that
follows local optimal choice at each step with the hope of finding global optimal
solution.
In many problems, it does not produce an optimal solution though it gives an
approximate (near optimal) solution in a reasonable time.
Algorithm Design Techniques 3.23
Areas of Application
Greedy approach is used to solve many problems, such as
Finding the shortest path between two vertices using Dijkstra’s algorithm.
Finding the minimal spanning tree in a graph using Prim’s /Kruskal’s
algorithm, etc.
Optimal Substructure:
An optimal solution to the problem contains within it optimal solutions to sub-
problems. A' = A - {1} (greedy choice) A' can be solved again with the greedy
algorithm. S' = { i ? S, s i ? f i }
The 0 - 1 knapsack problem:
A thief has a knapsack that holds at most W pounds. Item i : (v i , w i ) ( v = value,
w = weight ) thief must choose items to maximize the value stolen and still fit into
the knapsack. Each item must be taken or left (0 – 1 ).
w1 = 10 v1 = 60 d1.= 6
w2 = 20 v2 = 100 d2.= 5
w3 = 30 v3 = 120 d3 = 4
where d is the value density
Greedy approach: Take all of 1, and all of 2: v 1 + v 2 = 160, optimal solution is to
take all of 2 and 3: v2 + v3= 220, other solution is to take all of 1 and 3 v 1 + v 3 =
180. All below 50 lbs.
When solving the 0 - 1 knapsack problem, empty space lowers the effective d of
the load. Thus each time an item is chosen for inclusion we must consider both
i included
i excluded
These are clearly overlapping sub-problems for different i's and so best solved by
DP!
Fig. 3.21. The greedy strategy does not work for the 0-1 knapsack problem (a) The thief
must select a subset of the three items shown whose weight must not exceed 50 pounds. (b)
The optimal subset includes items 2 and 3. any solution with item 1 is suboptimal, even
though item 1 has the greatest value per pound. (c) For the fractional knapsack problem,
taking the items in order of greatest value per pound yields an optimal solution
The activity selection problem is an optimization problem used to find the
maximum number of activities a person can perform if they can only work on one
Algorithm Design Techniques 3.25
Algorithm
We are provided with n activities; each activity has its own start and finish time.
In order to find the maximum number of non-conflicting activities, the following
steps need to be taken:
Sort the activities in ascending order based on their finish times.
Select the first activity from this sorted list.
Select a new activity from the list if its start time is greater than or equal to
the finish time of the previously selected activity.
Repeat the last step until all activities in the sorted list are checked.
Step 3: Select the next activity in the sorted list its ‘start’ time is greater than or
equal to the ‘finish’ time of the previously selected activity.
It is a pattern that relates to the merging of two or more sorted files in a single
sorted file. This type of merging can be done by the two-way merging method.
If we have two sorted files containing n and m records respectively then they
could be merged together, to obtain one sorted file in time O (n + m).
There are many ways in which pairwise merge can be done to get a single sorted
file. Different pairings require a different amount of computing time. The main thing
is to pairwise merge the n sorted files so that the number of comparisons will be less.
The formula of external merging cost is:
n
f (i) d(i)
i = 1
Where, f (i) represents the number of records in each file and d (i) represents the
depth.
Algorithm Design Techniques 3.27
Algorithm Tree(n)
Return least(list);
}
Example:
Given a set of unsorted files: 5, 3, 2, 7, 9, 13
Now, arrange these elements in ascending order: 2, 3, 5, 7, 9, 13
After this, pick two smallest numbers and repeat this until we left with only one
number.
Now follow following steps:
Step 1: Insert 2, 3
3.28 Algorithms
Step 2:
Step 3: Insert 5
Step 4: Insert 13
Step 6:
Huffman coding provides codes to characters such that the length of the code
depends on the relative frequency or weight of the corresponding character. Huffman
codes are of variable-length, and without any prefix (that means no code is a prefix
of any other). Any prefix-free binary code can be displayed or visualized as a binary
tree with the encoded characters stored at the leaves.
Huffman tree or Huffman coding tree defines as a full binary tree in which each
leaf of the tree corresponds to a letter in the given alphabet.
The Huffman tree is treated as the binary tree associated with minimum external
path weight that means, the one associated with the minimum sum of weighted path
lengths for the given set of leaves. So the goal is to construct a tree with the
minimum external path weight.
An example is given below-
Letter z k m c u d l e
Frequency 2 7 24 32 37 42 42 120
3.30 Algorithms
Huffman Code
Fig. 3.22.
Huffman (C)
1. n=|C|
2. Q C
3. for i=1 to n-1
Algorithm Design Techniques 3.31
4. do
5. z= allocate-Node ()
6. x= left[z]=Extract-Min(Q)
7. y= right[z] =Extract-Min(Q)
8. f [z]=f[x]+f[y]
9. Insert (Q, z)
10. return Extract-Min (Q)
*******************
UNIT IV
STATE SPACE SEARCH
ALGORITHMS
Backtracking: n-Queens problem - Hamiltonian Circuit Problem - Subset Sum
Problem – Graph colouring problem Branch and Bound: Solving 15-Puzzle problem
- Assignment problem - Knapsack Problem - Travelling Salesman Problem
Backtracking is one of the techniques that can be used to solve the problem. We
can write the algorithm using this strategy. It uses the Brute force search to solve the
problem, and the brute force search says that for the given problem, we try to make
all the possible solutions and pick out the best solution from all the desired solutions.
This rule is also followed in dynamic programming, but dynamic programming is
used for solving optimization problems. In contrast, backtracking is not used in
solving optimization problems. Backtracking is used when we have multiple
solutions, and we require all those solutions.
Backtracking name itself suggests that we are going back and coming forward; if
it satisfies the condition, then return success, else we go back again. It is used to
solve a problem in which a sequence of objects is chosen from a specified set so that
the sequence satisfies some criteria.
Fig. 4.1.
Suppose another path exists from node A to node C. So, we move from node A to
node C. It is also a dead-end, so again backtrack from node C to node A. We move
from node A to the starting node.
Fig. 4.2.
Fig. 4.3.
Now we will check any other path exists from the starting node. So, we move
from start node to the node D. Since it is not a feasible solution so we move from
State Space Search Algorithms 4.3
node D to node E. The node E is also not a feasible solution. It is a dead end so we
backtrack from node E to node D.
Suppose another path exists from node D to node F. So, we move from node D to
node F. Since it is not a feasible solution and it's a dead-end, we check for another
path from node F.
Fig. 4.4.
Fig. 4.5.
4.4 Algorithms
Suppose there is another path exists from the node F to node G so move from
node F to node G. The node G is a success node.
Algorithm:
If all squares are visited
print the solution
Else
(a) Add one of the next moves to solution vector and recursively check if this
move leads to a solution. (A Knight can make maximum eight moves. We
choose one of the 8 moves in this step).
(b) If the move chosen in the above step doesn't lead to a solution then remove
this move from the solution vector and try other alternative moves.
(c) If none of the alternatives work then return false (Returning false will
remove the previously added item in recursion and if false is returned by
the initial call of recursion then "no solution exists")
Terms related to the backtracking are:
Live node: The nodes that can be further generated are known as live
nodes.
E node: The nodes whose children are being generated and become a
success node.
Success node: The node is said to be a success node if it provides a
feasible solution.
Dead node: The node which cannot be further generated and also does
not provide a feasible solution is known as a dead node.
Many problems can be solved by backtracking strategy, and that problems satisfy
complex set of constraints, and these constraints are of two types:
Implicit constraint: It is a rule in which how each element in a tuple is
related.
Explicit constraint: The rules that restrict each element to be chosen
from the given set.
APPLICATIONS OF BACKTRACKING
N-queen problem
State Space Search Algorithms 4.5
Input:
The size of a chess board. Generally, it is 8. as (8 8 is the size of a normal chess
board.)
Output:
The matrix that represents in which row and column the N Queens can be placed.
If the solution does not exist, it will return false.
10000000
00000010
00001000
00000001
01000000
00010000
00000100
00100000
In this output, the value 1 indicates the correct place for the queens.
The 0 denotes the blank spaces on the chess board.
Algorithm
isValid(board, row, col)
Input: The chess board, row and the column of the board.
4.6 Algorithms
Output − True when placing a queen in row and place position is a valid or not.
Begin
if there is a queen at the left of current col, then
return false
if there is a queen at the left upper diagonal, then
return false
if there is a queen at the left lower diagonal, then
return false;
return true //otherwise it is valid place
End
solveNQueen(board, col)
Input − The chess board, the col where the queen is trying to be placed.
Output − The position matrix where queens are placed.
Begin
if all columns are filled, then
return true
for each row of the board, do
if isValid(board, i, col), then
set queen at place (i, col) in the board
if solveNQueen(board, col+1) = true, then
return true
otherwise remove queen from place (i, col) from board.
done
return false
End
Example
#include<iostream>
using namespace std;
#define N 8
State Space Search Algorithms 4.7
return true;
Output
10000000
00000010
00001000
00000001
01000000
State Space Search Algorithms 4.9
00010000
00000100
00100000
Input:
The adjacency matrix of a graph G(V, E).
Fig. 4.6.
Output:
The algorithm finds the Hamiltonian path of the given graph. For this case it is (0,
1, 2, 4, 3, 0). This graph has some other Hamiltonian paths.
If one graph has no Hamiltonian path, the algorithm should return false.
Algorithm
isValid(v, k)
Input − Vertex v and position k.
Output − Checks whether placing v in the position k is valid or not.
Begin
if there is no edge between node(k –1) to v, then
4.10 Algorithms
return false
if v is already taken, then
return false
return true; //otherwise it is valid
End
cycleFound(node k)
Input − node of the graph.
Output − True when there is a Hamiltonian Cycle, otherwise false.
Begin
if all nodes are included, then
if there is an edge between nodes k and 0, then
return true
else
return false;
for all vertex v except starting point, do
if isValid(v, k), then //when v is a valid edge
add v into the path
if cycleFound(k +1) is true, then
return true
otherwise remove v from the path
done
return false
End
Example
#include<iostream>
#define NODE 5
using namespace std;
int graph[NODE][NODE] = {
State Space Search Algorithms 4.11
{0, 1, 0, 1, 0},
{1, 0, 1, 1, 1},
{0, 1, 0, 0, 1},
{1, 1, 0, 0, 1},
{0, 1, 1, 1, 0},
};
/* int graph[NODE][NODE] = {
{0, 1, 0, 1, 0},
{1, 0, 1, 1, 1},
{0, 1, 0, 0, 1},
{1, 1, 0, 0, 0},
{0, 1, 1, 0, 0},
}; */
int path[NODE];
void displayCycle() {
cout<<"Cycle: ";
for (int i = 0; i < NODE; i++)
cout << path[i] << " ";
cout << path[0] << endl; //print the first vertex again
}
bool isValid(int v, int k) {
if (graph [path[k-1]][v] == 0) //if there is no edge
return false;
for (int i = 0; i < k; i++) //if vertex is already taken, skip that
if (path[i] == v)
4.12 Algorithms
return false;
return true;
}
bool cycleFound(int k) {
if (k == NODE) { //when all vertices are in the path
if (graph[path[k-1]][ path[0] ] == 1 )
return true;
else
return false;
}
for (int v = 1; v < NODE; v++) { //for all vertices except starting point
if (isValid(v,k)) { //if possible to add v in the path
path[k] = v;
if (cycleFound (k+1) == true)
return true;
path[k] = -1; //when k vertex will not in the solution
}
}
return false;
}
bool hamiltonianCycle() {
for (int i = 0; i < NODE; i++)
path[i] = -1;
path[0] = 0; //first vertex as 0
if ( cycleFound(1) == false ) {
cout << "Solution does not exist"<<endl;
return false;
State Space Search Algorithms 4.13
}
displayCycle();
return true;
}
int main() {
hamiltonianCycle();
}
Output
Cycle: 0 1 2 4 3 0
Algorithm
subsetSum(set, subset, n, subSize, total, node, sum)
Input − The given set and subset, size of set and subset, a total of the subset,
number of elements in the subset and the given sum.
Output − All possible subsets whose sum is the same as the given sum.
Begin
if total = sum, then
display the subset
//go for finding next subset
subsetSum(set, subset, , subSize-1, total-set[node], node+1, sum)
return
else
for all element i in the set, do
subset[subSize] := set[i]
subSetSum(set, subset, n, subSize+1, total+set[i], i+1, sum)
done
End
Example
#include <iostream>
using namespace std;
void displaySubset(int subSet[], int size) {
for(int i = 0; i < size; i++) {
cout << subSet[i] << " ";
}
cout << endl;
}
void subsetSum(int set[], int subSet[], int n, int subSize, int total, int nodeCount
,int sum) {
if( total == sum) {
State Space Search Algorithms 4.15
Output
10 7 18
10 5 20
5 18 12
20 15
4.16 Algorithms
Graph coloring problem is a special case of graph labeling. In this problem, each
node is colored into some colors. But coloring has some constraints. We cannot use
the same color for any adjacent vertices.
Fig. 4.7.
For solving this problem, we need to use the greedy algorithm, but it does not
guaranty to use minimum color.
Algorithm
graphColoring(graph)
State Space Search Algorithms 4.17
Example
#include<iostream>
#define NODE 6
using namespace std;
int graph[NODE][NODE] = {
{0, 1, 1, 1, 0, 0},
{1, 0, 0, 1, 1, 0},
{1, 0, 0, 1, 0, 1},
{1, 1, 1, 0, 1, 1},
{0, 1, 0, 1, 0, 1},
{0, 0, 1, 1, 1, 0}
};
void graphColoring() {
int color[NODE];
color[0] = 0; //Assign first color for the first node
bool colorUsed[NODE]; //Used to check whether color is used or not
for(int i = 1; i<NODE; i++)
color[i] = -1; //initialize all other vertices are unassigned
for(int i = 0; i<NODE; i++)
colorUsed[i] = false; //initially any colors are not chosen
for(int u = 1; u<NODE; u++) { //for all other NODE - 1 vertices
for(int v = 0; v<NODE; v++) {
if(graph[u][v]){
if(color[v] != -1) //when one color is assigned, make it unavailable
colorUsed[color[v]] = true;
}
}
int col;
for(col = 0; col<NODE; col++)
State Space Search Algorithms 4.19
Output
Node: 0, Assigned with Color: 0
Node: 1, Assigned with Color: 0
Node: 2, Assigned with Color: 1
Node: 3, Assigned with Color: 2
Node: 4, Assigned with Color: 1
Let's explore bound methods.
Global upper bound U - as noted, we never need more than n colours. We can use
n as the initial value of U
4.20 Algorithms
Fig. 4.8.
Finding the optimal solution to a board is equivalent to finding the shortest path
from the current board state to the solved board state. A common path finding
algorithm is A*, which traverses through the search space using a heuristic which
estimates the distance to the desired end state. While A* would work for solving the
15 Puzzle, its memory usage increases exponentially with the length of the solution.
This is reasonable for the 8 Puzzle, as the hardest boards take 31 moves to solve.
Fig. 4.9.
4.22 Algorithms
Fig. 4.10.
Hence, solving the 15 Puzzle with A* can require massive amounts of memory. A
better algorithm to use is a variant of A* called IDA*. Compared to A*, it is less
efficient as it can explore the same nodes multiple times, but its memory usage is
only linear to the solution length.
For a p q puzzle, there are (pq)!/2 number of boards in the search space. So, the
search space increases super-exponentially as the board size increases. This makes
optimally solving large puzzles very impractical (the 35 Puzzle is the largest
analyzed size that I could find).
Algorithm:
solve = (startGrid) ->
frontier = new PriorityQueue
frontier.enqueue(new SolverState(startGrid, []))
while not frontier.empty()
curState = frontier.dequeue()
if curState.solved
return curState.steps
candidateMoves = grid.validMoves()
for move in candidateMoves
nextGrid = grid.applyMove(move)
nextSteps = curState.steps.concat([move])
nextState = new SolverState(nextGrid, nextSteps)
State Space Search Algorithms 4.23
frontier.enqueue(nextState)
{
// start from bottom-right corner of matrix
for (int i = N - 1; i >= 0; i--)
for (int j = N - 1; j >= 0; j--)
if (puzzle[i][j] == 0)
return N - i;
}
// This function returns true if given
// instance of N*N - 1 puzzle is solvable
bool isSolvable(int puzzle[N][N])
{
// Count inversions in given puzzle
int invCount = getInvCount((int*)puzzle);
// If grid is odd, return true if inversion
// count is even.
if (N & 1)
return !(invCount & 1);
else // grid is even
{
int pos = findXPosition(puzzle);
if (pos & 1)
return !(invCount & 1);
else
return invCount & 1;
}
}
/* Driver program to test above functions */
State Space Search Algorithms 4.25
int main()
{
int puzzle[N][N] =
{
{12, 1, 10, 2},
{7, 11, 4, 14},
{5, 0, 9, 15}, // Value 0 is used for empty space
{8, 13, 6, 3},
};
/*
int puzzle[N][N] = {{1, 8, 2},
{0, 4, 3},
{7, 6, 5}};
int puzzle[N][N] = {
{13, 2, 10, 3},
{1, 12, 8, 4},
{5, 0, 9, 6},
{15, 14, 11, 7},
};
int puzzle[N][N] = {
{6, 13, 7, 10},
{8, 9, 11, 0},
{15, 2, 12, 5},
{14, 3, 1, 4},
};
int puzzle[N][N] = {
{3, 9, 1, 15},
4.26 Algorithms
Output
Solvable
Time Complexity : O(n 2)
Space Complexity: O(n)
An example job assignment problem. Green values show optimal job assignment
that is A-Job4, B-Job1 C-Job3, B-Job3 and D-Job4
Fig. 4.11.
Let us explore all approaches for this problem.
1. For each worker, we choose job with minimum cost from list of
unassigned jobs (take minimum entry from each row).
2. For each job, we choose a worker with lowest cost for that job from list
of unassigned workers (take minimum entry from each column).
Let’s take below example and try to calculate promising cost when Job 2 is
assigned to worker A.
Fig. 4.12.
Since Job 2 is assigned to worker A (marked in green), cost becomes 2 and Job 2
and worker A becomes unavailable (marked in red).
Fig. 4.13.
Now we assign job 3 to worker B as it has minimum cost from list of unassigned
jobs. Cost becomes 2 + 3 = 5 and Job 3 and worker B also becomes unavailable.
Fig. 4.14.
State Space Search Algorithms 4.29
Fig. 4.15.
Below diagram shows complete search space diagram showing optimal solution
path in green.
Fig. 4.16.
Complete Algorithm:
/* findMinCost uses Least() and Add() to maintain the
4.30 Algorithms
return;
}
for each child x of E
{
Add(x); // Add x to list of live nodes;
x->parent = E; // Pointer for path to root
}
}
}
int cost;
// contains Job ID
int jobID;
node->parent = parent;
node->workerID = x;
node->jobID = y;
return node;
State Space Search Algorithms 4.33
// store cost
min = costMatrix[i][j];
4.34 Algorithms
}
}
return cost;
}
// print Assignments
void printAssignments(Node *min)
{
if(min->parent==NULL)
return;
State Space Search Algorithms 4.35
printAssignments(min->parent);
cout << "Assign Worker " << char(min->workerID + 'A')
<< " to Job " << min->jobID << endl;
// Driver code
int main()
{
// x-cordinate represents a Worker
// y-cordinate represents a Job
int costMatrix[N][N] =
{
{9, 2, 7, 8},
{6, 4, 3, 7},
{5, 8, 1, 8},
{7, 6, 9, 4}
};
/* int costMatrix[N][N] =
{
{82, 83, 69, 92},
{77, 37, 49, 92},
{11, 69, 5, 86},
{ 8, 9, 98, 23}
};
*/
4.36 Algorithms
/* int costMatrix[N][N] =
{
{2500, 4000, 3500},
{4000, 6000, 3500},
{2000, 4000, 2500}
};*/
/*int costMatrix[N][N] =
{
{90, 75, 75, 80},
{30, 85, 55, 65},
{125, 95, 90, 105},
{45, 110, 95, 115}
};*/
return 0;
}
Output :
Assign Worker A to Job 1
Assign Worker B to Job 0
Assign Worker C to Job 2
Assign Worker D to Job 3
Optimal Cost is 13
State Space Search Algorithms 4.37
Optimal Solution
Select item 2 and Item 3 which will give us total value of 140 + 60 = 200 which is
the maximum value we can get among all valid combinations.
The diagram above shows a Knapsack that can hold up to a maximum weight
of 30 units. We have three items namely item1, item2, and item3. The values and
weights associated with each item are shown in the diagram. Now we have to fill the
knapsack in such a way so that the sum of the values of items filled in the knapsack
is maximum. If we try out all the valid combinations in which the total weight of the
filled items is less than or equal to 30, we will see that we can get the optimal answer
by selecting item2 and item3 in the knapsack which gives us a total value of 120.
{
Xi=1;
M=M-wi;
}
else if (wi > M && M>0)
{
Xi=M/wi;
M=0;
}
else
{
Xi=0;
}
Example:-
Let us consider that the capacity of the knapsack M = 60 and the list of provided
items are shown in the following table-
Item A B C D
Profit 280 100 120 120
Weight 40 10 20 24
Ratio (p i /w i ) 7 10 6 5
However, the provided items are not sorted based on (p i / w i ). After sorting, the
items are shown in the following table.
Item A B C D
Profit 100 280 120 120
Weight 10 40 20 24
Ratio (p i /w i ) 10 7 6 5
Xi 1 1 10/20 0
State Space Search Algorithms 4.39
Solution:
Afterward, sorting all the items according to (pi/wi), first item B is chosen as the
weight of B is less than the capacity of the knapsack. Next, item A is chosen, as the
available capacity of the knapsack is greater than the weight of A. Then, C is chosen
as the next item. However, the whole item cannot be chosen as the remaining
capacity of the knapsack is less than the weight of C. Hence, fraction of C (i.e.(60-
50) / 20) is chosen.
Chiefly, the capacity of the knapsack is equal to the selected items. Hence, no
more items can be selected. So, the total weight of the selected items is 10 + 40 +
20*(10 / 20) = 60. Also, the total profit is 100 + 280 + 120*(10 / 20) = 380 + 60 =
440.Hence, this is the optimal solution. Moreover, we cannot gain more profit by
selecting any different combination of items.
#include<stdio.h>
#include<conio.h>
void main( )
{
int p[10],w[10],k[10],n,m,i,j,temp1,temp2,temp3;
float x[10],profit;
clrscr( );
printf(“Enter weight of knapsack\ n”);
scanf(“%d”,&m};
printf(“Enter no. of items\n”);
scanf(“%d”,&n”);
printf(“enter profit and weight of each item \ n”);
printf(“profit weight \ n”);
for(i=0;<n;i++)
{
scanf(“%d\t%d”,&p[i],&w[i]);
}
for(i=0;i<n;i++)
4.40 Algorithms
{
k[i]=p[i]/w[i];
}
for(i=0;i<n–1;i++)
{
for(j=0;j<n-1;j++)
{
if(k[j]<k[j+1])
{
temp1=k[j];
k[j]=k[j+1];
k[j+1]=temp1;
temp2=p[j];
p[j]=p[j+1];
p[j+1]=temp2;
temp3=w[j];
w[j]=w[j+1];
w[j+1]=temp3;
}
}
}
for(i=0;i<n;++)
{
if(w[i]<=m)
{
x[i]=1;
m=m–w[i];
}
State Space Search Algorithms 4.41
else if(w[i]>m&&m>0)
{
x[i]=(float)m/w[i];
m=0;
}
else
{
x[i]=0;
}
}
Output:-
Enter weight of knapsack
60
Enter no. of items
4
enter profit and weight of each item
profit weight
280 40
100 10
120 20
120 24
Selection of items
selection=1.000000
selection=1.000000
selection=0.500000
selection=0.000000
Maximum profit is 440.000000
4.42 Algorithms
Fig. 4.17.
For example, consider the graph shown in the figure on the right side. A TSP tour
in the graph is 1 – 2 – 4 – 3 –1. The cost of the tour is 10 + 25 + 30 + 15 which is 80.
The problem is a famous NP-hard problem. There is no polynomial-time know
solution for this problem. The following are different solutions for the traveling
salesman problem.
Naive Solution:
1. Consider city 1 as the starting and ending point.
2. Generate all (n – 1)! Permutations of cities.
3. Calculate the cost of every permutation and keep track of the minimum
cost permutation.
4. Return the permutation with minimum cost.
Time Complexity: Θ(n!)
State Space Search Algorithms 4.43
};
// memoization for top down recursion
int memo[n + 1][1 << (n + 1)];
int fun(int i, int mask)
{
// base case
// if only ith bit and 1st bit is set in our mask,
// it implies we have visited all other nodes already
if (mask == ((1 << i) | 3))
return dist[1][i];
// memoization
if (memo[i][mask] != 0)
return memo[i][mask];
int res = MAX; // result of this sub-problem
// we have to travel all nodes j in mask and end the
// path at ith node so for every node j in mask,
// recursively calculate cost of travelling all nodes in
// mask except i and then travel back from node j to
// node i taking the shortest path take the minimum of
// all possible j nodes
for (int j = 1; j <= n; j++)
if ((mask & (1 << j)) && j != i && j != 1)
res = std::min(res, fun(j, mask & (~(1 << i)))
+ dist[j][i]);
return memo[i][mask] = res;
}
// Driver program to test above logic
int main()
{
State Space Search Algorithms 4.45
Output
The cost of most efficient tour = 80
For a set of size n, we consider n – 2 subsets each of size n –1 such that all
subsets don’t have nth in them. Using the above recurrence relation, we can write a
dynamic programming-based solution. There are at most O(n* 2 n) subproblems,
and each one takes linear time to solve. The total running time is therefore O( n *
2 n). The time complexity is much less than O(n!) but still exponential. The space
required is also exponential.
4.46 Algorithms
1. What is Backtracking
Backtracking is one of the techniques that can be used to solve the problem.
We can write the algorithm using this strategy. It uses the Brute force search to
solve the problem, and the brute force search says that for the given problem, we
try to make all the possible solutions and pick out the best solution from all the
desired solutions.
2. When to use a Backtracking algorithm?
When we have multiple choices, then we make the decisions from the
available choices. In the following cases, we need to use the backtracking
algorithm:
A piece of sufficient information is not available to make the best choice,
so we use the backtracking strategy to try out all the possible solutions.
Each decision leads to a new set of choices. Then again, we backtrack to
make new decisions. In this case, we need to use the backtracking
strategy.
3. List the applications of Backtracking.
N-queen problem
Sum of subset problem
Graph coloring
Hamiliton cycle
4. What is meant by n-Queens problem.
This problem is to find an arrangement of N queens on a chess board, such
that no queen can attack any other queens on the board. The chess queens can
attack in any direction as horizontal, vertical, horizontal and diagonal way. A
binary matrix is used to display the positions of N Queens, where no queens can
attack other queens.
5. State Hamiltonian Circuit Problem
In an undirected graph, the Hamiltonian path is a path, that visits each vertex
exactly once, and the Hamiltonian cycle or circuit is a Hamiltonian path, that
there is an edge from the last vertex to the first vertex. In this problem, we will
try to determine whether a graph contains a Hamiltonian cycle or not.
State Space Search Algorithms 4.47
10. What are the two approaches to calculate the cost function?
For each worker, we choose job with minimum cost from list of
unassigned jobs (take minimum entry from each row).
For each job, we choose a worker with lowest cost for that job from list
of unassigned workers (take minimum entry from each column).
11. What is 0-1 Knapsack Problem
In the 0-1 Knapsack Problem, we are given a Knapsack or a Bag that can hold
weight up to a certain value. We have various items that have different weights
and values associated with them. Now we have to fill the knapsack in such a
way so that the sum of the total weights of the filled items does not exceed the
maximum capacity of the knapsack and the sum of the values of the filled items
is maximum.
12. Define a TSP.
Given a set of cities and the distance between every pair of cities, the
problem is to find the shortest possible route that visits every city exactly once
and returns to the starting point. Note the difference between Hamiltonian
Cycle and TSP. The Hamiltonian cycle problem is to find if there exists a tour
that visits every city exactly once. Here we know that Hamiltonian Tour exists
(because the graph is complete) and in fact, many such tours exist, the problem
is to find a minimum weight Hamiltonian Cycle.
Examples
Towers of Hanoi: we can prove that any algorithm that solves this problem must
have a worst-case running time that is at least 2n − 1.
* List all permutations (all possible orderings) of n numbers.
Fig. 5.1.
It turns out that the most interesting class of problems is a class, which lies in
some sense between the class of tractable problems P and those of the provably
intractable, super-polynomial time problems.
The Hamiltonian cycle and Travelling Salesman Problems belong to the class of
NP- Complete, which is a subset of the larger problem class NP. NP-Complete is a
class of problems whose time-complexity is presently unknown, though strongly
believed to be super-polynomial.
Input:
W (Capacity of Knapsack)
val[0..n – 1] (Values of Items)
wt[0..n – 1] (Weights of Items)
1. Find the maximum valued item, i.e., find maximum value in val[]. Let this
maximum value be maxVal.
2. Compute adjustment factor k for all values
k = (maxVal * ε) / n
3. Adjust all values, i.e., create a new array val'[] that values divided by k. Do
following for every value val[i].
val'[i] = floor(val[i] / k)
4. Run DP based solution for reduced values, i,e, val'[0..n – 1] and all other
parameter same.
The above solution works in polynomial time in terms of both n and ε. The
solution provided by this FPTAS is (1 – ε) approximate. The idea is to rounds off
some of the least significant digits of values then they will be bounded by a
polynomial and 1/ε.
5.8 Algorithms
Example:
val[] = {12, 16, 4, 8}
wt[] = {3, 4, 5, 2}
W = 10
ε = 0.5
maxVal = 16 [maximum value in val[]]
Adjustment factor, k = (16 * 0.5)/4 = 2.0
Now we apply DP based solution on below modified instance of problem.
val'[] = {6, 8, 2, 4} [ val'[i] = floor(val[i]/k) ]
wt[] = {3, 4, 5, 2}
W = 10
Venn Diagram
A Venn diagram is used to visually represent the differences and the similarities
between two concepts. Venn diagrams are also called logic or set diagrams and are
widely used in set theory, logic, mathematics, businesses, teaching, computer
science, and statistics.
What is a Venn Diagram?
A Venn diagram is a diagram that helps us visualize the logical relationship
between sets and their elements and helps us solve examples based on these sets. A
Venn diagram typically uses intersecting and non-intersecting circles (although other
closed figures like squares may be used) to denote the relationship between sets.
Universal Set
Whenever we use a set, it is easier to first consider a larger set called a universal
set that contains all of the elements in all of the sets that are being considered.
Whenever we draw a Venn diagram:
A large rectangle is used to represent the universal set and it is usually
denoted by the symbol E or sometimes U.
All the other sets are represented by circles or closed figures within this
larger rectangle.
Every set is the subset of the universal set U.
Fig. 5.4.
Consider the above-given image:
U is the universal set with all the numbers 1-10, enclosed within the
rectangle.
A is the set of even numbers 1-10, which is the subset of the universal set
U and it is placed inside the rectangle.
All the numbers between 1-10, that are not even, will be placed outside the
circle and within the rectangle as shown above.
Subset
Venn diagrams are used to show subsets. A subset is actually a set that is
contained within another set. Let us consider the examples of two sets A and B in the
NP - Complete and Approximation Algorithm 5.11
Let us understand the concept and the usage of the three basic Venn diagram
symbols using the image given below.
Fig. 5.6.
Total Elements (No. of
Symbol It refers to
students)
A∪C The number of students 1 + 10 + 2 + 2 + 6 + 9 = 30
that prefer either burger or
pizza or both.
A∩C The number of students 2+2=4
that prefer both burger and
pizza.
A∩B∩C The number of students 2
that prefer a burger, pizza
as well as hotdog.
Ac or A' The number of students 10 + 6 + 9 = 25
that do not prefer a burger.
Step 2: Draw a rectangle and label it as per the correlation between the
two sets. Here, let's label the rectangle as Pets.
Step 3: Draw the circles according to the number of categories you have.
There are two categories in the sample question: outdoor pets and indoor
pets. So, let us draw two circles and make sure the circles overlap.
Fig. 5.7.
Step 4: Place all the pets in the relevant circles. If there are certain pets
that fit both the categories, then place them at the intersection of sets,
where the circles overlap. Rabbits and fish can be kept as indoor and
outdoor pets, and hence they are placed at the intersection of both circles.
Here, n(A) and n(B) represent the number of elements in A and B respectively.
n(A U B) and n(A ⋂ B) represent the number of elements in A U B and A ⋂ B
respectively. This formula is further extended to 3 sets as well and it says:
n (A U B U C) = n(A) + n(B) + n(C) – n(A ⋂ B) – n(B ⋂ C) – n(C ⋂ A) +
n(A ⋂ B ⋂ C)
Here is an example of Venn diagram formula.
Example: In a cricket school, 12 players like bowling, 15 like batting, and 5 like
both. Then how many players like either bowling or batting.
Solution:
Let A and B be the sets of players who like bowling and batting respectively. Then
n(A) = 12
n(B) = 15
n(A ⋂ B) = 5
We have to find n(A U B). Using the Venn diagram formula,
n(A U B) = n(A) + n(B) – n (A ⋂ B)
n(A U B) = 12 + 15 - 5 = 22.
Venn diagrams can be used to reason through the logic behind statements
or equations.
There are computational problems that can not be solved by algorithms even with
unlimited time. For example Turing Halting problem (Given a program and an input,
whether the program will eventually halt when run with that input, or will run
forever). Alan Turing proved that a general algorithm to solve the halting problem
for all possible program-input pairs cannot exist. A key part of the proof is, that the
Turing machine was used as a mathematical definition of a computer and program.
Status of NP-Complete problems is another failure story, NP-complete problems
are problems whose status is unknown. No polynomial-time algorithm has yet been
discovered for any NP-complete problem, nor has anybody yet been able to prove
that no polynomial-time algorithm exists for any of them. The interesting part is, that
if any one of the NP-complete problems can be solved in polynomial time, then all of
them can be solved.
Fig. 5.9.
In computer science, there exist some problems whose solutions are not yet found,
the problems are divided into classes known as Complexity Classes. In complexity
theory, a Complexity Class is a set of problems with related complexity. These
classes help scientists to groups problems based on how much time and space they
require to solve problems and verify the solutions. It is the branch of the theory of
computation that deals with the resources required to solve a problem.
The common resources are time and space, meaning how much time the algorithm
takes to solve a problem and the corresponding memory usage. The time
complexity of an algorithm is used to describe the number of steps required to
solve a problem, but it can also be used to describe how long it takes to verify the
answer. The space complexity of an algorithm describes how much memory is
required for the algorithm to operate.
P Class
The P in the P class stands for Polynomial Time. It is the collection of decision
problems(problems with a “yes” or “no” answer) that can be solved by a
deterministic machine in polynomial time.
Features:
1. The solution to P problems is easy to find.
5.18 Algorithms
Co-NP Class
Co-NP stands for the complement of NP Class. It means if the answer to a
problem in Co-NP is No, then there is proof that can be checked in polynomial time.
Features:
1. If a problem X is in NP, then its complement X‟ is also is in CoNP.
2. For an NP and CoNP problem, there is no need to verify all the answers at
once in polynomial time, there is a need to verify only one particular
answer “yes” or “no” in polynomial time for a problem to be in NP or
CoNP.
Some example problems for C0-NP are:
1. To check prime number.
2. Integer Factorization.
NP-hard class
An NP-hard problem is at least as hard as the hardest problem in NP and it is the
class of the problems such that every problem in NP reduces to NP-hard.
Features:
1. All NP-hard problems are not in NP.
2. It takes a long time to check them. This means if a solution for an NP-
hard problem is given then it takes a long time to check whether it is
right or not.
3. A problem A is in NP-hard if, for every problem L in NP, there exists a
polynomial-time reduction from L to A.
Some of the examples of problems in Np-hard are:
1. Halting problem.
2. Qualified Boolean formulas.
3. No Hamiltonian cycle.
5.20 Algorithms
NP-complete class
A problem is NP-complete if it is both NP and NP-hard. NP-complete problems
are the hard problems in NP.
Features:
1. NP-complete problems are special as any problem in NP class can be
transformed or reduced into NP-complete problems in polynomial time.
2. If one could solve an NP-complete problem in polynomial time, then one
could also solve any NP problem in polynomial time.
Some example problems include:
1. Decision version of 0/1 Knapsack.
2. Hamiltonian Cycle.
3. Satisfiability.
4. Vertex cover.
Complexity
Class Characteristic feature
P Easily solvable in polynomial time.
NP Yes, answers can be checked in polynomial time.
Co-NP No, answers can be checked in polynomial time.
All NP-hard problems are not in NP and it takes a long
NP-hard time to check them.
NP-complete A problem that is NP and NP-hard is NP-complete.
Given n items of different weights and bins each of capacity c, assign each item
to a bin such that number of total used bins is minimized. It may be assumed that
all items have weights smaller than bin capacity.
Example:
Input: weight[] = {4, 8, 1, 4, 2, 1}
NP - Complete and Approximation Algorithm 5.21
Bin Capacity c = 10
Output: 2
We need minimum 2 bins to accommodate all items
First bin contains {4, 4, 2} and second bin {8, 1, 1}
Input: weight[] = {9, 8, 2, 2, 5, 4}
Bin Capacity c = 10
Output: 4
We need minimum 4 bins to accommodate all items.
Input: weight[] = {2, 5, 4, 7, 1, 3, 8};
Bin Capacity c = 10
Output: 3
Lower Bound
We can always find a lower bound on minimum number of bins required. The
lower bound can be given as :
Min no. of bins >= Ceil ((Total Weight) / (Bin Capacity))
In the above examples, lower bound for first example is “ceil(4 + 8 + 1 + 4 + 2 +
1)/10′′ = 2 and lower bound in second example is “ceil(9 + 8 + 2 + 2 + 5 + 4)/10′′ =
3.
This problem is a NP Hard problem and finding an exact minimum number of
bins takes exponential time. Following are approximate algorithms for this
problem.
Applications
1. Loading of containers like trucks.
2. Placing data on multiple disks.
3. Job scheduling.
4. Packing advertisements in fixed length radio/TV station breaks.
5. Storing a large collection of music onto tapes/CD‟s, etc.
5.22 Algorithms
Online Algorithms
These algorithms are for Bin Packing problems where items arrive one at a time
(in unknown order), each must be put in a bin, before considering the next item.
1. Next Fit:
When processing next item, check if it fits in the same bin as the last item. Use a
new bin only if it does not.
Below is C++ implementation for this algorithm.
C++ program to find number of bins required using next fit algorithm.
#include <bits/stdc++.h>
using namespace std;
// Returns number of bins required using next fit
// online algorithm
int nextFit(int weight[], int n, int c)
{
// Initialize result (Count of bins) and remaining
// capacity in current bin.
int res = 0, bin_rem = c;
// Place items one by one
for (int i = 0; i < n; i++) {
// If this item can't fit in current bin
if (weight[i] > bin_rem) {
res++; // Use a new bin
bin_rem = c - weight[i];
}
else
bin_rem -= weight[i];
}
return res;
}
NP - Complete and Approximation Algorithm 5.23
// Driver program
int main()
{
int weight[] = { 2, 5, 4, 7, 1, 3, 8 };
int c = 10;
int n = sizeof(weight) / sizeof(weight[0]);
cout << "Number of bins required in Next Fit : "
<< nextFit(weight, n, c);
return 0;
Output:
Number of bins required in Next Fit : 4
Reduction
Problem decomposition refers to the problem-solving process that computer
scientists apply to solve a complex problem by breaking it down into parts that can
be more easily solved. Oftentimes, this involves modifying algorithm templates and
combining multiple algorithm ideas them to solve the problem at hand: all of the
design work in the projects have been designed with this process in mind.
However, modifying an algorithm template is just one way that we can solve a
problem. An alternative approach is to represent it as a different problem, one that
can be solved without modifying an algorithm. Reduction is a problem
decomposition approach where an algorithm designed for one problem can be used
to solve another problem.
1. Modify the input so that it can be framed in terms of another problem.
2. Solve the modified input using a standard (unmodified) algorithm.
3. Modify the output of the standard algorithm to solve the original problem.
5.24 Algorithms
Genetic algorithms are heuristic search algorithms inspired by the process that
supports the evolution of life. The algorithm is designed to replicate the natural
selection process to carry generation, i.e. survival of the fittest of beings. Standard
genetic algorithms are divided into five phases which are: Creating initial
population.
1. Calculating fitness.
2. Selecting the best genes.
3. Crossing over.
4. Mutating to introduce variations.
These algorithms can be implemented to find a solution to the optimization
problems of various types. One such problem is the Traveling Salesman Problem.
The problem says that a salesman is given a set of cities, he has to find the shortest
route to as to visit each city exactly once and return to the starting city.
Approach: In the following implementation, cities are taken as genes, string
generated using these characters is called a chromosome, while a fitness score which
is equal to the path length of all the cities mentioned, is used to target a population.
Fitness Score is defined as the length of the path described by the gene. Lesser the
path length fitter is the gene. The fittest of all the genes in the gene pool survive the
population test and move to the next iteration. The number of iterations depends
upon the value of a cooling variable. The value of the cooling variable keeps on
decreasing with each iteration and reaches a threshold after a certain number of
iterations.
Algorithm:
1. Initialize the population randomly.
2. Determine the fitness of the chromosome.
3. Until done repeat:
1. Select parents.
2. Perform crossover and mutation.
3. Calculate the fitness of the new population.
5.26 Algorithms
Fig. 5.10.
This chromosome undergoes mutation. During mutation, the position of two cities
in the chromosome is swapped to form a new configuration, except the first and the
last cell, as they represent the start and endpoint.
Fig. 5.11.
Original chromosome had a path length equal to INT_MAX, according to the
input defined below, since the path between city 1 and city 4 didn‟t exist. After
mutation, the new child formed has a path length equal to 21, which is a much-
NP - Complete and Approximation Algorithm 5.27
optimized answer than the original assumption. This is how the genetic algorithm
optimizes solutions to hard problems.
Example:
Given a set of cities and the distance between each pair of cities, the travelling
salesman problem finds the path between these cities such that it is the shortest path
and traverses every city once, returning back to the starting point.
Problem – Given a graph G(V, E), the problem is to determine if the graph has
a TSP consisting of cost at most K.
Explanation –.
In order to prove the Travelling Salesman Problem is NP-Hard, we will have to
reduce a known NP-Hard problem to this problem. We will carry out a reduction
from the Hamiltonian Cycle problem to the Travelling Salesman problem.
Every instance of the Hamiltonian Cycle problem consists of a graph G = (V, E)
as the input can be converted to a Travelling Salesman problem consisting of graph
G′ = (V′, E′) and the maximum cost, K. We will construct the graph G′ in the
following way:
For all the edges e belonging to E, add the cost of edge c(e) =1. Connect the
remaining edges, e‟ belonging to E‟, that are not present in the original graph G, each
with a cost c(e′) = 2.
And, set
The new graph G′ can be constructed in polynomial time by just converting G to a
complete graph G‟ and adding corresponding costs. This reduction can be proved by
the following two claims:
Let us assume that the graph G contains a Hamiltonian Cycle, traversing
all the vertices V of the graph. Now, these vertices form a TSP
with. Since it uses all the edges of the original graph having cost c(e) =
1. And, since it is a cycle, therefore, it returns back to the original vertex.
We assume that the graph G‟ contains a TSP with cost, The TSP
traverses all the vertices of the graph returning to the original vertex.
Now since none of the vertices are excluded from the graph and the cost
sums to n, therefore, necessarily it uses all the edges of the graph present
in E, with cost 1, hence forming a hamiltonian cycle with the graph G.
5.28 Algorithms
Fig. 5.12.
Thus we can say that the graph G’ contains a TSP if graph G contains
Hamiltonian Cycle. Therefore, any instance of the Travelling salesman problem can
be reduced to an instance of the hamiltonian cycle problem. Thus, the TSP is NP-
Hard.
The point of 3CNF is that it is a "normal form" for formulas: as you mention,
every formula is equivalent, up to a quadratic (linear?) blowup, to a 3CNF formula.
3CNF formulas are "simple", and so more easy to deal with. In particular, if you ever
read about NP-completeness, you will find out that we want our to put our
"challenging problems" in as simple a form as possible. This makes it easier both to
design and analyze algorithms solving these problems, and to prove that other
problems are also difficult by reducing 3CNF to them (showing how to solve 3CNF
using an algorithm for them).
We care specifically about 3CNF for historical reasons, it was the first (or one of
the first) NP-complete problems (check out Cook's paper or Karp's paper on their
respective pages). Also, 2CNF is not "complete" (arbitrary formulas cannot are not
equivalent to a 2CNF), and it is easy to determine whether they are satisfiable or not
(google if interested).
The conversion from CNF to 3CNF is best explained by an example. We convert
each clause separately. The clause A∨ B∨ C∨ D∨ EA∨ B∨ C∨ D∨ E is equivalent to
the 3CNF
NP - Complete and Approximation Algorithm 5.29
arbitrary formula to a CNF can result in exponential blow-up, for example for parity
on nn variables we go from Θ( n 2)Θ(n 2) to Θ(n 2 n)Θ(n 2 n).
THEOREM 1
If P!= NP, there exists no c-approximation algorithm for the traveling salesman
problem, i.e., there exists no polynomial-time approximation algorithm for this
problem so that for all instances
f (sa) c f (s*)
for some constant c.
PROOF
By way of contradiction, suppose that such an approximation algorithm A and a
constant c exist. (Without loss of generality, we can assume that c is a positive
integer.) We will show that this algorithm could then be used for solving the
Hamiltonian circuit problem in polynomial time. We will take advantage of a
variation of the transformation used in Section 11.3 to reduce the Hamiltonian circuit
problem to the traveling salesman problem. Let G be an arbitrary graph
with n vertices. We map G to a complete weighted graph G by assigning weight 1 to
each edge in G and adding an edge of weight cn + 1 between each pair of vertices not
adjacent in G. If G has a Hamiltonian circuit, its length in G is n; hence, it is the
exact solution s * to the traveling salesman problem for G .
Note that if sa is an approximate solution obtained for G by algorithm A, then f
(sa) ≤ cn by the assumption. If G does not have a Hamiltonian circuit in G, the
shortest tour in G will contain at least one edge of weight cn + 1, and hence f (sa) ≥ f
(s*) > cn. Taking into account the two derived inequalities, we could solve the
Hamiltonian circuit problem for graph G in polynomial time by mapping G to G ,
NP - Complete and Approximation Algorithm 5.31
applying algorithm A to get tour sa in G , and comparing its length with cn. Since the
Hamiltonian circuit problem is NP-complete, we have a contradiction unless P = NP.
Nearest-neighbor algorithm
The following well-known greedy algorithm is based on the nearest-
neighbor heuristic: always go next to the nearest unvisited city.
Step 1 Choose an arbitrary city as the start.
Step 2 Repeat the following operation until all the cities have been
visited: go to the unvisited city nearest the one visited last (ties can
be broken arbitrarily).
Step 3 Return to the starting city.
EXAMPLE 1 For the instance represented by the graph in Figure , with a as the
starting vertex, the nearest-neighbor algorithm yields the tour (Hamiltonian
circuit) sa: a − b − c − d − a of length 10.
f (sa) 10
r(sa) = f (s*) = 8 = 1.25
yield the tour a – b – c – d − a of length 4 + w, and the optimal solution will still
be a − b − d − c − a of length 8. Hence,
f (sa) 4 + w
r (sa) = f (s*) = 8
How many times while loop runs before finding a central pivot?
The probability that the randomly chosen element is central pivot is 1/ n.
Therefore, expected number of times the while loop runs is n (See this for details)
Thus, the expected time complexity of step 2 is O(n).
What is overall Time Complexity in Worst Case?
In worst case, each partition divides array such that one side has n/4 elements and
other side has 3n / 4 elements. The worst case height of recursion tree is Log 3/4 n
which is O(Log n).
T(n) < T(n/4) + T(3n/4) + O(n)
T(n) < 2T(3n/4) + O(n)
Solution of above recurrence is O(n Log n)
5.34 Algorithms
Note that the above randomized algorithm is not the best way to implement
randomized Quick Sort. The idea here is to simplify the analysis as it is simple to
analyse.
Typically, randomized Quick Sort is implemented by randomly picking a pivot
(no loop). Or by shuffling array elements. Expected worst case time complexity of
this algorithm is also O(n Log n), but analysis is complex, the MIT prof himself
mentions same in his lecture here.
test m upto √n. If n is composite then it can be factored into two values, at
least one of which should be less than or same to √n.
Probabilistic Algorithm − A probabilistic algorithm provide an answer
that is correct most of time, but not all of the time. These tests decided
whether n satisfies one or more conditions that all primes should satisfy. A
probabilistic algorithm restore either a prime or a composite depends on
the following rules −
o If the integer to be tested is actually a prime, the algorithm
definitely return a prime.
o If the integer to be tested is actually a composite, it returns a
composite with probability 1 − ε, but it can return a prime with the
probability ε. The probability of mistakes can be enhanced if it can
run the algorithm „m‟ times and the probability of error reduce to
Σm.
while (n > 0)
if (n & 1)
res = (res*a) % p;
n = n>>1; // n = n/2
a = (a*a) % p;
return res;
NP - Complete and Approximation Algorithm 5.37
if(a < b)
else if(a%b == 0)
return b;
// result.
{
// Corner cases
// Try k times
while (k>0)
int a = 2 + rand()%(n-4);
if (gcd(n, a) != 1)
return false;
if (power(a, n-1, n) != 1)
return false;
k--;
return true;
int main()
{
int k = 3;
isPrime(11, k)? cout << " true\n": cout << " false\n";
isPrime(15, k)? cout << " true\n": cout << " false\n";
return 0;
}
Output:
true
false
NP - Complete and Approximation Algorithm 5.39
// increment index of
// smaller element
i++;
swap(arr[i], arr[j]);
}
}
swap(arr[i + 1], arr[high]);
return (i + 1);
}
// Generates Random Pivot, swaps pivot with
// end element and calls the partition function
int partition_r(int arr[], int low, int high)
{
// Generate a random number in between
// low .. high
srand(time(NULL));
int random = low + rand() % (high - low);
5.42 Algorithms
{
int i;
for (i = 0; i < size; i++)
cout<<arr[i]<<" ";
}
// Driver Code
int main()
{
int arr[] = { 10, 7, 8, 9, 1, 5 };
int n = sizeof(arr) / sizeof(arr[0]);
quickSort(arr, 0, n - 1);
printf("Sorted array: \n");
printArray(arr, n);
return 0;
}
Output
Sorted array:
1 5 7 8 9 10
Given an array and a number K where K is smaller than the size of the array. Find
the K‟th smallest element in the given array. Given that all array elements are
distinct.
Examples:
Input: arr[] = {7, 10, 4, 3, 20, 15}, K = 3
5.44 Algorithms
Output: 7
Input: arr[] = {7, 10, 4, 3, 20, 15}, K = 4
Output: 10
{
int arr[] = { 12, 3, 5, 7, 19 };
int N = sizeof(arr) / sizeof(arr[0]), K = 2;
// Function call
printf("K'th smallest element is %d",
kthSmallest(arr, N, K));
return 0;
}
// This code is contributed by Sania Kumari Gupta
// (kriSania804)
Output
Kth smallest element is 5
Time Complexity: O(N log N)
Auxiliary Space: O(1)
5.46 Algorithms
Complement of set
Difference of set
8. How to Draw a Venn Diagram?
Venn diagrams can be drawn with unlimited circles. Since more than three
becomes very complicated, we will usually consider only two or three circles in
a Venn diagram. Here are the 4 easy steps to draw a Venn diagram:
Step 1: Categorize all the items into sets.
Step 2: Draw a rectangle and label it as per the correlation between the
sets.
Step 3: Draw the circles according to the number of categories you have.
Step 4: Place all the items in the relevant circles.
9. State in brief about NP- algorithms.
There are computational problems that can not be solved by algorithms even
with unlimited time. For example Turing Halting problem (Given a program
and an input, whether the program will eventually halt when run with that
input, or will run forever). Alan Turing proved that a general algorithm to
solve the halting problem for all possible program-input pairs cannot exist. A
key part of the proof is, that the Turing machine was used as a mathematical
definition of a computer and program.
10. What are NP, P, NP-complete, and NP-Hard problems?
P is a set of problems that can be solved by a deterministic Turing machine in
Polynomial-time.
NP is a set of decision problems that can be solved by a Non-deterministic
Turing Machine in Polynomial-time. P is a subset of NP (any problem that can
be solved by a deterministic machine in polynomial time can also be solved by a
non-deterministic machine in polynomial time).
Informally, NP is a set of decision problems that can be solved by a
polynomial-time via a “Lucky Algorithm”, a magical algorithm that always
makes a right guess among the given set of choices.
11. Define a P Class.
The P in the P class stands for Polynomial Time. It is the collection of
decision problems(problems with a “yes” or “no” answer) that can be solved by
a deterministic machine in polynomial time.
NP - Complete and Approximation Algorithm 5.49
Quick Sort, we use a random number to pick the next pivot (or we randomly
shuffle the array). And in Karger‟s algorithm, we randomly pick an edge.
21. How to find kth smallest number.
Given an array and a number K where K is smaller than the size of the array.
Find the Kth smallest element in the given array. Given that all array elements
are distinct.
Examples:
Input: arr[] = {7, 10, 4, 3, 20, 15}, K = 3
Output: 7
Input: arr[] = {7, 10, 4, 3, 20, 15}, K = 4
Output: 10
Program
#include<stdio.h>
#include<conio.h>
#include<time.h>
#include<stdlib.h>
#define max 20
int pos;
int linsearch (int,int[],int);
void main()
{
int ch=1; double t;
int n,i,a [max],k,op,low,high,pos; clock_tbegin,end;
clrscr();
while(ch)
{
printf("\n.......MENU \n 1. Linear search \n 2.Exit \n");
printf("\n enter your choice\n");
scanf("%d",&op);
switch(op)
{
case 1:printf("\n enter the number of elements \n");
scanf("%d",&n);
P.2 Algorithms
return linsearch(n-1,a,k);
}
MENU
.Linear search
.Exit
enter your choice
1
enter the number of elements
3
enter the number of an array in the order
25 69 98
enter the elements to be searched
98
element 98 is found at position 3
Time Taken is 1.978022 CPU1 cycles
Program
#include<stdio.h>
#include<conio.h>
#include<time.h>
#include<stdlib.h>
#define max 20
int pos;
Int binsearch (int,int[],int,int,int);
void main()
P.4 Algorithms
{
int ch=1;
double t;
int n,i,a [max],k,op,low,high,pos;
clock_tbegin,end;
clrscr();
while(ch)
{
printf("\n.......MENU \n 1.BinarySearch \n 2.Exit \n");
printf("\n enter your choice\n"); scanf("%d",&op);
switch(op)
{
case 1:printf("\n enter the number of elments\n"); scanf("%d",&n);
printf("\n enter the number of an array in the order \n"); for(i=0;i<n;i++)
scanf("%d",&a[i]);
printf("\n enter the elements to be searched \n"); scanf("%d",&k);
low=0;high=n-1; begin=clock(); pos=binsearch(n,a,k,low,high);
end=clock();
if(pos==-1) printf("\n\nUnsuccessful search");
else
printf("\n element %d is found at position %d",k,pos+1);
printf("\n Time Taken is %lf CPU1 cycles \n",(end-begin)/CLK_TCK); getch();
break;
}
printf("\n Do you wish to run again(1/0) \n"); scanf("%d",&ch);
}
getch();
}
Int binsearch(int n,int a[],int k,int low,int high)
Practical Exercises P.5
{
int mid; delay(1000); mid=(low+high)/2; if(low>high) return -1; if(k==a[mid])
return(mid);
else
if(k<a[mid])
return binsearch(n,a,k,low,mid-1);
else return binsearch(n,a,k,mid+1,high);
}
MENU
.BinarySearch
.Exit
enter your choice
1
enter the number of elements
3
enter the number of an array in the order
98
22
46
enter the elements to be searched
22
element 22 is found at position 2
Time Taken is 1.978022 CPU cycles
3. Given a text txt [0...n – 1] and a pattern pat [0...m – 1], write a
function search (char pat [ ], char txt [ ]) that prints all occurrences of
pat [ ] in txt [ ]. You may assume that n > m.
P.6 Algorithms
if(flag):
print("Pattern found at index {}".format(i))
print("Example-1:")
txt = "AABAACAADAABAABA"
pat = "AABA"
naive_pattern_search(pat, txt)
print("Example-2:")
txt = "abracadabra"
pat = "ab"
naive_pattern_search(pat, txt)
Example-2:
Pattern found at index 0
Pattern found at index 7
Practical Exercises P.7
4. Sort a given set of elements using the Heap sort methods and
determine the time required to sort the elements. Repeat the
experiment for different values of n, the number of elements in the list
to be sorted and plot a graph of the time taken versus n.
Program
#include<stdio.h>
#include<conio.h>
#include<time.h>
void heapsort(int n,int arr[]);
void heapy(int n,int arr[]);
void adjust(int n,int arr[]);
void heapsort(int n,int arr[])
{
inti,item; delay(100); heapy(n,arr);
for(i=n;i>=1;i--)
{
item=arr[1]; arr[1]=arr[i]; arr[i]=item; adjust(i,arr);
}
}
void heapy(int n,int arr[])
{
inti,j,k,item;
for(i=1;i<=n;i++)
{
item=arr[i]; j=i;
k=j/2;
while(k!=0 && item>arr[k])
{
arr[j]=arr[k]; j=k;
P.8 Algorithms
k=j/2;
}
arr[j]=item;
}
}
void adjust(int n,int arr[])
{
inti,j,item; j=1;
item=arr[j]; i=j*2; while(i<n)
{
if((i+1)<n)
{
if(arr[i]<arr[i+1])
i++;
}
if(item<arr[i])
{
arr[j]=arr[i]; j=i;
i=2*j;
}
else break;
}
arr[j]=item;
}
void main()
{
int i,n,arr[20];
clock_tend,start;
clrscr();
Practical Exercises P.9
Program
#include <stdio.h>
#include <stdlib.h>
#define SIZE 40
P.10 Algorithms
struct queue {
int items[SIZE];
int front;
int rear;
};
struct node {
int vertex;
struct node* next;
};
struct Graph {
int numVertices;
struct node** adjLists;
int* visited;
};
// BFS algorithm
void bfs(struct Graph* graph, int startVertex) {
struct queue* q = createQueue();
Practical Exercises P.11
graph->visited[startVertex] = 1;
enqueue(q, startVertex);
while (!isEmpty(q)) {
printQueue(q);
int currentVertex = dequeue(q);
printf("Visited %d\n", currentVertex);
while (temp) {
int adjVertex = temp->vertex;
if (graph->visited[adjVertex] == 0) {
graph->visited[adjVertex] = 1;
enqueue(q, adjVertex);
}
temp = temp->next;
}
}
}
// Creating a node
struct node* createNode(int v) {
struct node* newNode = malloc(sizeof(struct node));
newNode->vertex = v;
newNode->next = NULL;
return newNode;
P.12 Algorithms
// Creating a graph
struct Graph* createGraph(int vertices) {
struct Graph* graph = malloc(sizeof(struct Graph));
graph->numVertices = vertices;
int i;
for (i = 0; i < vertices; i++) {
graph->adjLists[i] = NULL;
graph->visited[i] = 0;
}
return graph;
}
// Add edge
void addEdge(struct Graph* graph, int src, int dest) {
// Add edge from src to dest
struct node* newNode = createNode(dest);
newNode->next = graph->adjLists[src];
graph->adjLists[src] = newNode;
graph->adjLists[dest] = newNode;
}
// Create a queue
struct queue* createQueue() {
struct queue* q = malloc(sizeof(struct queue));
q->front = -1;
q->rear = -1;
return q;
}
if (isEmpty(q)) {
printf("Queue is empty");
} else {
printf("\nQueue contains \n");
for (i = q->front; i < q->rear + 1; i++) {
printf("%d ", q->items[i]);
Practical Exercises P.15
}
}
}
int main() {
struct Graph* graph = createGraph(6);
addEdge(graph, 0, 1);
addEdge(graph, 0, 2);
addEdge(graph, 1, 2);
addEdge(graph, 1, 4);
addEdge(graph, 1, 3);
addEdge(graph, 2, 4);
addEdge(graph, 3, 4);
bfs(graph, 0);
return 0;
}
Output
Queue contains
0 Resetting queue Visited 0
Queue contains
2 1 Visited 2
Queue contains
1 4 Visited 1
Queue contains
4 3 Visited 4
Queue contains
3 Resetting queue Visited 3
P.16 Algorithms
Program
#include <stdio.h>
#include <stdlib.h>
struct node {
int vertex;
struct node* next;
};
struct Graph {
int numVertices;
int* visited;
// DFS algo
void DFS(struct Graph* graph, int vertex) {
struct node* adjList = graph->adjLists[vertex];
struct node* temp = adjList;
graph->visited[vertex] = 1;
printf("Visited %d \n", vertex);
Practical Exercises P.17
// Add edge
void addEdge(struct Graph* graph, int src, int dest) {
// Add edge from src to dest
struct node* newNode = createNode(dest);
newNode->next = graph->adjLists[src];
graph->adjLists[src] = newNode;
addEdge(graph, 1, 2);
addEdge(graph, 2, 3);
printGraph(graph);
DFS(graph, 2);
return 0;
}
Output
Adjacency list of vertex 0
21
Adjacency list of vertex 1
20
Adjacency list of vertex 2
310
Adjacency list of vertex 3
2
Visited 2
Visited 3
Visited 1
Visited 0
Program
#include <stdio.h>
#define INFINITY 9999
#define MAX 10
void Dijkstra(int Graph[MAX][MAX], int n, int start);
void Dijkstra(int Graph[MAX][MAX], int n, int start) {
P.20 Algorithms
pred[i] = nextnode;
}
count++;
}
// Printing the distance
for (i = 0; i < n; i++)
if (i != start) {
printf("\nDistance from source to %d: %d", i, distance[i]);
}
}
int main() {
int Graph[MAX][MAX], i, j, n, u;
n = 7;
Graph[0][0] = 0;
Graph[0][1] = 0;
Graph[0][2] = 1;
Graph[0][3] = 2;
Graph[0][4] = 0;
Graph[0][5] = 0;
Graph[0][6] = 0;
Graph[1][0] = 0;
Graph[1][1] = 0;
Graph[1][2] = 2;
Graph[1][3] = 0;
Graph[1][4] = 0;
Graph[1][5] = 3;
Graph[1][6] = 0;
P.22 Algorithms
Graph[2][0] = 1;
Graph[2][1] = 2;
Graph[2][2] = 0;
Graph[2][3] = 1;
Graph[2][4] = 3;
Graph[2][5] = 0;
Graph[2][6] = 0;
Graph[3][0] = 2;
Graph[3][1] = 0;
Graph[3][2] = 1;
Graph[3][3] = 0;
Graph[3][4] = 0;
Graph[3][5] = 0;
Graph[3][6] = 1;
Graph[4][0] = 0;
Graph[4][1] = 0;
Graph[4][2] = 3;
Graph[4][3] = 0;
Graph[4][4] = 0;
Graph[4][5] = 2;
Graph[4][6] = 0;
Graph[5][0] = 0;
Graph[5][1] = 3;
Graph[5][2] = 0;
Graph[5][3] = 0;
Graph[5][4] = 2;
Practical Exercises P.23
Graph[5][5] = 0;
Graph[5][6] = 1;
Graph[6][0] = 0;
Graph[6][1] = 0;
Graph[6][2] = 0;
Graph[6][3] = 1;
Graph[6][4] = 0;
Graph[6][5] = 1;
Graph[6][6] = 0;
u = 0;
Dijkstra(Graph, n, u);
return 0;
}
Output
Distance from source to 1: 3
Distance from source to 2: 1
Distance from source to 3: 2
Distance from source to 4: 4
Distance from source to 5: 4
Distance from source to 6: 3
Program
#include<stdio.h>
#include<stdbool.h>
P.24 Algorithms
return 0;
}
P.26 Algorithms
Output
Edge : Weight
0-1:9
1 - 3 : 19
3 - 4 : 31
3 - 2 : 51
Program
#include<iostream>
#include<iomanip>
#define NODE 7
#define INF 999
using namespace std;
//Cost matrix of the graph
int costMat[NODE][NODE] = {
{0, 3, 6, INF, INF, INF, INF},
{3, 0, 2, 1, INF, INF, INF},
{6, 2, 0, 1, 4, 2, INF},
{INF, 1, 1, 0, 2, INF, 4},
{INF, INF, 4, 2, 0, 2, 1},
{INF, INF, 2, INF, 2, 0, 1},
{INF, INF, INF, 4, 1, 1, 0}
};
void floydWarshal(){
int cost[NODE][NODE]; //defind to store shortest distance from any node to
any node
for(int i = 0; i<NODE; i++)
Practical Exercises P.27
Output
The matrix:
0354677
3021344
5201323
4110233
6332021
7423201
7433110
P.28 Algorithms
Program
#include<stdio.h>
// Number of vertices in the graph
#define V 4
// A function to print the solution matrix
void printSolution(int reach[][V]);
// Prints transitive closure of graph[][]
// using Floyd Warshall algorithm
void transitiveClosure(int graph[][V])
{
/* reach[][] will be the output matrix
// that will finally have the
shortest distances between
every pair of vertices */
int reach[V][V], i, j, k;
for (i = 0; i < V; i++)
for (j = 0; j < V; j++)
reach[i][j] = graph[i][j];
for (k = 0; k < V; k++)
{
// Pick all vertices as
// source one by one
for (i = 0; i < V; i++)
{
// Pick all vertices as
// destination for the
// above picked source
Practical Exercises P.29
}
}
int main()
{
/* Let us create the following weighted graph
10
(0)------->(3)
| /|\
5| |
| |1
\|/ |
(1)------->(2)
3
int graph[V][V] = { {1, 1, 0, 1},
{0, 1, 1, 0},
{0, 0, 1, 1},
{0, 0, 0, 1}
};
Output
Following matrix is transitive closure of the given graph
1111
0111
0011
0001
Practical Exercises P.31
Program
#include <iostream>
#include <vector>
#include <climits>
using namespace std;
void findMinAndMax(vector<int> const &nums, int low, int high, int &min, int
&max)
{
if (low == high) // common comparison
{
if (max < nums[low]) { // comparison 1
max = nums[low];
}
return;
}
{
if (min > nums[low]) { // comparison 2
min = nums[low];
}
int main()
Practical Exercises P.33
{
vector<int> nums = { 7, 2, 9, 3, 1, 6, 7, 8, 4 };
int n = nums.size();
findMinAndMax(nums, 0, n - 1, min, max);
cout << "The minimum array element is " << min << endl;
cout << "The maximum array element is " << max << endl;
return 0;
}
Output:
The minimum array element is 1
The maximum array element is 9
Program
#include <stdio.h>
#include <stdlib.h>
void merge(int arr[], int l, int m, int r)
{
int i, j, k;
int n1 = m - l + 1;
int n2 = r - m;
int L[n1], R[n2];
P.34 Algorithms
Output
Given array is
12 11 13 5 6 7
Sorted array is
5 6 7 11 12 13
Program
#define N 4
#include <stdbool.h>
#include <stdio.h>
void printSolution(int board[N][N])
{
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++)
printf(" %d ", board[i][j]);
printf("\n");
}
}
bool isSafe(int board[N][N], int row, int col)
{
int i, j;
for (i = 0; i < col; i++)
if (board[row][i])
return false;
for (i = row, j = col; i >= 0 && j >= 0; i--, j--)
if (board[i][j])
return false;
Practical Exercises P.37
}
printSolution(board);
return true;
}
int main()
{
solveNQ();
return 0;
}
Output
..Q.
Q...
...Q
.Q..
1. Implement any scheme to find the optimal solution for the Traveling
Salesperson problem and then solve the same problem instance using
any approximation algorithm and determine the error in the
approximation.
Program
#include<stdio.h>
int a[10][10],n,visit[10];
int cost_opt=0,cost_apr=0;
int least_apr(int c);
int least_opt(int c);
int i,ncity;
visit[city]=1;
printf("%d-->",city);
ncity=least_opt(city);
if(ncity==999)
{
ncity=1;
printf("%d",ncity);
cost_opt+=a[city][ncity];
return;
}
mincost_opt(ncity);
}
void mincost_apr(int city)
{
int i,ncity;
visit[city]=1;
printf("%d-->",city);
ncity=least_apr(city);
if(ncity==999)
{
ncity=1;
printf("%d",ncity);
cost_apr+=a[city][ncity];
return;
}
mincost_apr(ncity);
}
P.40 Algorithms
int least_opt(int c)
{
int i,nc=999;
int min=999,kmin=999;
for(i=1;i<=n;i++)
{
if((a[c][i]!=0)&&(visit[i]==0))
if(a[c][i]<min)
{
min=a[i][1]+a[c][i];
kmin=a[c][i];
nc=i;
}
}
if(min!=999)
cost_opt+=kmin;
return nc;
}
int least_apr(int c)
{
int i,nc=999;
int min=999,kmin=999;
for(i=1;i<=n;i++)
{
if((a[c][i]!=0)&&(visit[i]==0))
if(a[c][i]<kmin)
{
min=a[i][1]+a[c][i];
Practical Exercises P.41
kmin=a[c][i];
nc=i;
}
}
if(min!=999)
cost_apr+=kmin;
return nc;
}
void main()
{
int i,j;
printf("Enter No. of cities:\n");
scanf("%d",&n);
OUTPUT:
Enter No. of cities:
4
Enter the cost matrix
Enter elements of row:1
0136
Enter elements of row:2
1023
Enter elements of row:3
3201
Enter elements of row:4
6310
Practical Exercises P.43
1 0 2 3
3 2 0 1
6 3 1 0
Optimal Solution :
The path is :
12431
Minimum cost:8
Approximated Solution :
The path is :
12341
Minimum cost:10
Error in approximation is approximated solution/optimal solution = 1.250000
Program
#include<iostream>
#include<climits>
#include<cstdlib>
using namespace std;
{
if (k > 0 && k <= r - l + 1)
{
int pos = randomPartition(arr, l, r);
if (pos-l == k-1)
return arr[pos];
if (pos-l > k-1)
return kthSmallest(arr, l, pos-1, k);
return INT_MAX;
}
void swap(int *a, int *b)
{
int temp = *a;
*a = *b;
*b = temp;
}
int partition(int arr[], int l, int r)
{
int x = arr[r], i = l;
for (int j = l; j <= r - 1; j++)
{
if (arr[j] <= x)
{
swap(&arr[i], &arr[j]);
Practical Exercises P.45
i++;
}
}
swap(&arr[i], &arr[r]);
return i;
}
int main()
{
int arr[] = {12, 3, 5, 7, 4, 19, 26};
int n = sizeof(arr)/sizeof(arr[0]), k = 3;
cout << "K'th smallest element is " << kthSmallest(arr, 0, n-1, k);
return 0;
}
Output
K'th smallest element is 5
Model Question Paper - 1
B.E./B.Tech DEGREE EXAMINATION.,
Fourth Semester
CS3401 - ALGORITHMS
(Regulations 2021)
Time: Three Hours Maximum: 100 Marks
Answer ALL Questions
PART – A (10 2 = 20 Marks)
1. What is Algorithm analysis?
Analyzing an algorithm has come to mean predicting the resources that the
algorithm requires. Occasionally, resources such as memory, communication
band-width, or computer hardware are of primary concern, but most often it is
computational time that we want to measure.
2. What is Rabin-Karp-Algorithm?
The Rabin-Karp string matching algorithm calculates a hash value for the
pattern, as well as for each M-character subsequences of text to be compared. If
the hash values are unequal, the algorithm will determine the hash value for next
M-character sequence. If the hash values are equal, the algorithm will analyze
the pattern and the M-character sequence. In this way, there is only one
comparison per text subsequence, and character matching is only required when
the hash values match.
3. What are the 2 types of graphs representations?
Sequential representation (or, Adjacency matrix representation)
Linked list representation (or, Adjacency list representation)
4. What is Bellman Ford algorithm?
Bellman Ford algorithm works by overestimating the length of the path from
the starting vertex to all other vertices. Then it iteratively relaxes those estimates
by finding new paths that are shorter than the previously overestimated paths.
MQ.2 Algorithms
particular, if you ever read about NP-completeness, you will find out that we
want our to put our "challenging problems" in as simple a form as possible. This
makes it easier both to design and analyze algorithms solving these problems,
and to prove that other problems are also difficult by reducing 3CNF to them
PART-B (5 13 = 65 Marks)
11. (a) Explain in detail about Knuth-Morris-Pratt algorithm
Ans: Refer Page No.1.22
[OR]
(b) Explain briefly about Binary search and Interpolation search
Ans: Refer Page No.1.15
12. (a) Explain the concept of Depth First Search and Breadth First Search in detail.
Ans: Refer Page No. 2.3
[OR]
(b) Briefly explain Floyd-Warshall algorithm
Ans: Refer Page No.2.26
13. (a) Explain in detail about Greedy Technique.
Ans: Refer Page No.3.23
[OR]
(b) Explain the concept of Matrix-chain multiplication.
Ans: Refer Page No.3.9
14. (a) Summarize Hamiltonian Circuit Problem.
Ans: Refer Page No.4.9
[OR]
(b) How do you solve a Solving 15-Puzzle problem. Elaborate.
Ans: Refer Page No.4.20
15. (a) Explain Bin Packing problem with an example.
Ans: Refer Page No.5.20
[OR]
MQ.4 Algorithms
PART - C (1 15 = 15 Marks)
***************
Model Question Papers MQ.5
PART-B (5 13 = 65 Marks)
11. (a) Explain Insertion sort with an example.
Ans: Refer Page No.1.29
MQ.8 Algorithms
[OR]
(b) Explain in detail about Asymptotic Notations and its properties.
Ans: Refer Page No.1.2
12. (a) Explain graph representations in detail
Ans: Refer Page No.2.1
[OR]
(b) Write in detail about Maximum bipartite matching.
Ans: Refer Page No.2.31
13. (a) Explain Optimal Binary Search Trees with an example.
Ans: Refer Page No.3.18
[OR]
(b) Explain Huffman Trees with an example.
Ans: Refer Page No.3.29
14. (a) Elaborate Subset Sum Problem with an example
Ans: Refer Page No.4.13
[OR]
(b) Elucidate the Assignment problem with an example
Ans: Refer Page No.4.26
15. (a) Classify in detail NP-hardness and NP-completeness.
Ans: Refer Page No.5.16
[OR]
(b) Elaborate the concept and application of Randomized Algorithms.
Ans: Refer Page No.5.32
PART - C (1 15 = 15 Marks)
16. (a) Explain with an algorithm the efficiency of Graph colouring problem.
Ans: Refer Page No.4.16
[OR]
(b) Write in detail about Prim’s algorithm.
Ans: Refer Page No.2.18