DAA Unit 2
DAA Unit 2
Syllabus
Binary Search- Introduction, Applications: Median of two sorted arrays, Find the fixed
point in a given array, Find Smallest Common Element in All Rows, Longest Common
Prefix, Koko Eating Bananas.
I. Binary Search
Introduction:
Given a sorted array arr[] of n elements, write a function to search a given element x in
arr[].
The time complexity of above algorithm is O(n). Another approach to perform the same
task is using Binary Search.
A binary search or half-interval search algorithm finds the position of a specified value
(the input "key") within a sorted array.
In each step, the algorithm compares the input key value with the key value of the middle
element of the array.
If the keys match, then a matching element has been found so its index, or position, is
returned.
Otherwise, if the sought key is less than the middle element's key, then the algorithm
repeats its action on the sub-array to the left of the middle element or, if the input key is
greater, on the sub-array to the right.
If the remaining array to be searched is reduced to zero, then the key cannot be found in
the array and a special "Not found" indication is returned.
Every iteration eliminates half of the remaining possibilities. This makes binary searches
very efficient - even for large collections.
Binary search requires a sorted collection. Also, binary searching can only be applied to a
collection that allows random access (indexing).
Working:
Binary Search Algorithm can be implemented in two ways which are discussed below.
1. Recursive Method
2. Iterative Method
T(n) = T(n/2) + c2
=T(n/4)+c2+c2
=T(n/8) +c2+c2+c2
…..
…..
k
= T( n / 2 ) + c2+c2+c2+ ………..k times
= T(1)+ kc2
= c1+kc2 =c1+ logn*c2 = O(logn)
Successful searches:
best average worst
O(1) O(log n) O( log n)
Unsuccessful searches :
best average worst
O(log n) O(log n) O( log n)
import java.util.*;
class BinarySearch_iterative
{
int binarySearch(int array[], int x, int low, int high)
{
// Repeat until the pointers low and high meet each other
while (low <= high)
{
int mid = low + (high - low) / 2;
if (array[mid] == x)
return mid;
if (array[mid] < x)
low = mid + 1;
else
high = mid - 1;
}
return -1;
}
int n = sc.nextInt();
for(int i=0;i<n;i++)
{
array[i] =sc.nextInt();
}
// Applying sort() method over to above array by passing the array as an argument
Arrays.sort(array);
BinarySearch_recursive.java
import java.util.*;
class BinarySearch_recursive
{
int binarySearch(int array[], int x, int low, int high) {
return -1;
}
int n = sc.nextInt();
for(int i=0;i<n;i++)
{
KMIT Page no: 7
DESIGN AND ANALYSIS OF ALGORITHMS UNIT-II
array[i] =sc.nextInt();
}
// Applying sort() method over to above array
// by passing the array as an argument
Arrays.sort(array);
Output:
Case=1
enter array size5
enter the elements of array 15
35
25
95
65
sorted array[]:[15, 25, 35, 65, 95]
Enter the key
65
Element found at index 3
Case=2
enter array size5
enter the elements of array 15
35
25
95
65
sorted array[]:[15, 25, 35, 65, 95]
Enter the key
30
Element Not found
Applications:
Examples:
Constraints:
nums1.length == m
nums2.length == n
0 <= m <= 1000
0 <= n <= 1000
1 <= m + n <= 2000
-106 <= nums1[i], nums2[i] <= 106
The key idea to note here is that both the arrays are sorted. Therefore, this leads us to
think of binary search. Let us try to understand the algorithm using an example:
A[ ] = {1, 4, 7} B[ ] = {2, 3, 5, 6}
From the above diagram, it can be easily deduced that only the first half of the array is needed
and the right half can be discarded.
KMIT Page no: 10
DESIGN AND ANALYSIS OF ALGORITHMS UNIT-II
Similarly, for an even length merged array, ignore the right half and only the left
half contributes to our final answer.
Therefore, the motive of our approach is to find which of the elements from both the
array helps in contributing to the final answer. Therefore, the binary search comes to the
rescue, as it can discard a part of the array every time, the elements don’t contribute to
the median.
Algorithm
As discussed earlier, we just need to find the elements contributing to the left half of the
array.
Since, the arrays are already sorted, it can be deduced that
A[i – 1] < A[i] and B[i – 1] < B[i].
Therefore, we just need to find the index i, such that
A[i – 1] <= B[j] and B[j – 1] <= A[i].
Consider mid = (n + m – 1) / 2 and check if this satisfies the above condition.
If A[i-1] <= B[j] and B[j-1]<=A[i] satisfies the condition, return the index i.
If A[i] < B[j – 1], increase the range towards the right. Hence update i = mid + 1.
Similarly, if A[i – 1] > B[j], decrease the range towards left. Hence update i
= mid – 1.
If the size of any of the arrays is 0, return the median of the non-zero sized array.
If the size of smaller array is 1,:
If the size of the larger array is also one, simply return the median as the mean of
both the elements.
Else, if size of larger array is odd, adding the element from first array will result in
size even, hence median will be affected if and only if, the element of the first array
lies between , M / 2th and M/2 + 1th element of B[].
Similarly, if the size of the larger array is even, check for the element of the smaller
array, M/2th element and M / 2 + 1 th element.
If the size of smaller array is 2,
If a larger array has an odd number of elements, the median can be either
the middle element or the median of elements of smaller array and M/2 -
1th element or minimum of the second element of A[] and M/2+ 1 th array.
MedianOfTwoSortedArrays.java
import java.util.*;
public class MedianOfTwoSortedArrays {
// Edge cases there are no elements left on the left side after partition
int n = sc.nextInt();
for(int i=0;i<n;i++)
{
nums1[i] =sc.nextInt();
}
System.out.println("enter the size of the 2nd array");
int m = sc.nextInt();
for(int i=0;i<m;i++)
{
nums2[i] =sc.nextInt();
}
}
}
Output:
enter the size of the 1st array
4
enter the elements of 1st array
1
4
7
8
enter the size of the 2nd array
4
enter the elements of 2nd array
2
3
5
6
The Median of two sorted arrays is :4.5
Given an array of n distinct integers sorted in ascending order, write a function that
returns a Fixed Point in the array, if there is any Fixed Point present in array, else
returns -1.
Fixed Point in an array is an index i such that arr[i] is equal to i. Note that integers in
array can be negative.
Example 1:
Input: [-10,-5,0,3,7]
Output: 3
Explanation:
For the given array, A[0] = -10, A[1] = -5, A[2] = 0, A[3] = 3, thus the output is 3.
Example 2:
Input: [0,2,5,8,17]
Output: 0
Explanation:
A[0] = 0, thus the output is 0.
Example 3:
Input: [-10,-5,3,4,7,9]
Output: -1
Explanation:
There is no such i that A[i] = i, thus the output is -1.
Note:
1 <= A.length < 10^4
-10^9 <= A[i] <= 10^9
Algorithm
The basic idea of binary search is to divide n elements into two roughly equal parts, and
compare a[n/2] with x.
Findfixedpoint.java
import java.util.*;
class Findfixedpoint
{
static int fixedpoint(int arr[], int low, int high)
{
if (high >= low) {
int mid = low + (high - low) / 2;
if (mid == arr[mid])
return mid;
int res = -1;
if (mid + 1 <= arr[high])
res = fixedpoint(arr, (mid + 1), high);
if (res != -1)
return res;
if (mid - 1 >= arr[low])
return fixedpoint(arr, low, (mid - 1));
}
// main function
public static void main(String args[])
{
for(int i=0;i<n;i++)
{
array[i] =sc.nextInt();
Arrays.sort(array);
Output:
case=1
enter array size
10
enter the elements of array
11 30 50 0 3 100 -10 -1 10 102
sorted array[]:[-10, -1, 0, 3, 10, 11, 30, 50, 100, 102]
Fixed Point is 3
case=2
enter array size
6
enter the elements of array
3 9 4 7 -5 -10
sorted array[]:[-10, -5, 3, 4, 7, 9]
Fixed Point is -1
case=3
enter array size
5
enter the elements of array
8 2 5 17 0
sorted array[]:[0, 2, 5, 8, 17]
Fixed Point is 0
Given a matrix where every row is sorted in increasing order. Write a function that finds and
returns a smallest common element in all rows. If there is no common element, then returns -
1.
Example-1:
Input: mat [4][5] = { {1, 2, 3, 4, 5},
{2, 4, 5, 8, 10},
{3, 5, 7, 9, 11},
{1, 3, 5, 7, 9}
};
Output: 5
Time complexity:
A O(m*n*n) simple solution is to take every element of first row and search it in all
other rows, till we find a common element.
Time complexity of this solution is O(m*n*n) where m is number of rows and n is
number of columns in given matrix.
This can be improved to O(m*n*logn) if we use Binary Search instead of linear
search.
Program: SmallestCommonElement.java
import java.util.*;
class SmallestCommonElement
{
private boolean binarySearch(int[] arr, int low, int high, int target)
{
while(low <= high) {
int mid = (low + high)/2;
if(arr[mid] == target)
{
return true;
}
else if(arr[mid] < target)
{
low = mid+1;
} else {
high = mid-1;
}
}
return false;
}
for(int i=0;i<m;i++)
for(int j=0;j<n;j++)
arr[i][j] = sc.nextInt();
case=2
input=
enter size of two dimensional matrix
enter row size of matrix
2
enter column size of matrix
2
enter elements for matrix
12
34
output=
smallest comman element :-1
Problem Statement:
Given the array of strings S[], you need to find the longest string S which is the prefix of
ALL the strings in the array.
Longest common prefix (LCP) for a pair of strings S1 and S2 is the longest string S
which is the prefix of both S1 and S2.
For Example: longest common prefix of “abcdefgh” and “abcefgh” is “ABC”.
Examples:
Input: S[] = {“abcdefgh”, “abcefgh”}
Output: “abc”
Explanation: Explained in the image description above
Algorithm:
Consider the string with the smallest length. Let the length be L.
Consider a variable low = 0 and high = L – 1.
Perform binary search:
Divide the string into two halves, i.e. low – mid and mid + 1 to high.
Compare the substring upto the mid of this smallest string to every other
character of the remaining strings at that index.
If the substring from 0 to mid – 1 is common among all the substrings,
update low with mid + 1, else update high with mid – 1
If low == high, terminate the algorithm and return the substring from 0 to mid.
Java program for LongestCommonPrefix using Binary search approach
LongestCommonPrefix.java
import java.util.*;
class LongestCommonPrefix {
public String longestCommonPrefix(String[] strs)
{
if (strs == null || strs.length == 0)
return "";
return longestCommonPrefix(strs, 0 , strs.length - 1);
}
input:
Enter Strings
fly flower flow
output:
Longest common Prefix is: fl
input:
Enter Strings
fci
output:
Longest common Prefix is:
Koko loves to eat bananas. There are n piles of bananas, the ith pile has piles[i] bananas.
The guards have gone and will come back in h hours.
Koko can decide her bananas-per-hour eating speed of k. Each hour, she chooses some
pile of bananas and eats k bananas from that pile. If the pile has less than k bananas,
she eats all of them instead and will not eat any more bananas during this hour.
Koko likes to eat slowly but still wants to finish eating all the bananas before the guards
return.
Return the minimum integer k such that she can eat all the bananas within h hours.
Example 1:
Input: piles = [3,6,7,11], h = 8
Output: 4
Example 2:
Input: piles = [30,11,23,4,20], h = 5
Output: 30
Example 3:
Input: piles = [30,11,23,4,20], h = 6
Output: 23
Input:
piles = [30,11,23,4,20], H = 6
output:
23
Koko will eat bananas in this way to eat all bananas in 6 hours:
First hour: 23
Second hour: 7
Third hour: 11
Fourth hour: 23
The first and the most important thing to solve this problem is to bring out observations. Here are
a few observations for our search interval:
1. Koko must eat at least one banana per hour. So this is the minimum value of K. let’s
name it as Start
2. We can limit the maximum number of bananas Koko can eat in one hour to the maximum
number of bananas in a pile out of all the piles. So this is the maximum value of K. let’s
name it as End.
Now we have our search interval. Suppose the size of the interval is Length and the number of
piles is n. The naive approach could be to check for each value in the interval. if for that value
of K Koko can eat all bananas in H hour successfully then pick the minimum of them. The time
complexity for the naive approach will be Length*n in worst case.
We can improve the time complexity by using Binary Search in place of Linear Search.
The time complexity using the Binary Search approach will be log(Length)*n.
Time complexity:
The time complexity of the above code is O(n*log(W)) because we are performing a binary
search between one and W this takes logW time and for each value, in the binary search, we are
traversing the piles array. So the piles array is traversed logW times it makes the time
complexity n*logW. Here n and W are the numbers of piles and the maximum number of
bananas in a pile.
Space complexity:
The space complexity of the above code is O(1) because we are using only a variable to store the
answer.
Applications:
1. Minimum product subset of an array
2. Best Time to Buy and Sell Stock
3. 0/1 Knapsack Problem
4. Minimum cost spanning trees
5. Single source shortest path Problem
Given an array a, we have to find the minimum product possible with the subset
of elements present in the array. The minimum product can be a single element also.
Examples:
Input : a[] = { 0, 0, 0 }
Output : 0
A simple solution is to generate all subsets, find the product of every subset
and return the minimum product.
->If there are even number of negative numbers and no zeros, the result is the product
of all except the largest valued negative number.
->If there are an odd number of negative numbers and no zeros, the result is simply
the product of all.
->If there are zeros and positive, no negative, the result is 0. The exceptional
case is when there is no negative number and all other elements positive then our
result should be the first minimum positive number.
Complexity Analysis:
import java.util.*;
class MinProductSubset
{
if (a[i] == 0) {
count_zero++;
continue;
}
// count the negative numbers and find the max negative number
if (a[i] < 0) {
count_neg++;
negmax = Math.max(negmax, a[i]);
}
product *= a[i];
}
// if there are all zeroes or zero is present but no negative number is present
return product;
}
case=1
input=
enter size of the array
5
enter elements
-1 -1 -2 4 3
output=
-24
case=2
input=
enter size of the array
2
enter elements
-1 0
output=
-1
case=3
input=
enter size of the array
3
enter elements
0 0 0
output=
0
Given an array prices[] of length N, representing the prices of the stocks on different days,
the task is to find the maximum profit possible for buying and selling the stocks on different
days using transactions where at most one transaction is allowed.
Java program for Best Time to Buy and Sell Stock with at most one transaction
BTBS_Atmostonetime.java
import java.util.*;
class BTBS_Atmostonetime
{
static int maxProfit(int prices[], int n)
{
int minprice=Integer.MAX_VALUE;
int max_profit = 0;
int size=sc.nextInt();
System.out.println("Enter elements");
for(int i=0;i<size;i++)
{
prices[i]=sc.nextInt();
}
int max_profit = maxProfit(prices,size);
System.out.println("Maximum Profit is:" + max_profit);
}
}
Output:
case=1
Enter array size
6
Enter elements
715364
Maximum Profit is:5
case=2
Enter array size
6
Enter elements
654321
Maximum Profit is:0
KMIT Page no: 36
DESIGN AND ANALYSIS OF ALGORITHMS UNIT-II
Given an array price[] of length N, representing the prices of the stocks on different days,
the task is to find the maximum profit possible for buying and selling the stocks on
different days using transactions where any number of transactions are allowed.
Explanation:
Purchase on 2nd day. Price = 1.
Sell on 3rd day. Price = 5.
Therefore, profit = 5 – 1 = 4.
Purchase on 4th day. Price = 3.
Sell on 5th day. Price = 6.
Therefore, profit = 4 + (6 – 3) = 7.
Approach: The idea is to maintain a boolean value that denotes if there is any current
purchase ongoing or not. If yes, then at the current state, the stock can be sold to
maximize profit or move to the next price without selling the stock. Otherwise,
if no transaction is happening, the current stock can be bought or move to the next
price without buying.
Java program for Best Time to Buy and Sell Stock with Infinite transactions :
BTBS_InfiniteTransactions.java
import java.util.*;
class BTBS_InfiniteTransactions
{
public static void main(String [] args)
{
Scanner sc = new Scanner(System.in);
int n=sc.nextInt();
int arr[] = new int[n];
for(int i=0;i<n;i++){
arr[i]=sc.nextInt();
}
System.out.println(prof(arr,n));
}
public static int prof(int arr[] , int n){
int profit=0;
for(int i=0;i<n-1;i++)
{
if(arr[i]<arr[i+1])
{
profit+=(arr[i+1]-arr[i]);
}
}
return profit;
}
}
input=
7
715364
Output=
7
Algorithm:
Analysis:
If the provided items are already sorted into a decreasing order of 𝒑𝒊/𝒘𝒊, then the while loop
takes a time in (𝒏); Therefore, the total time including the sort is in
𝑶(𝒏 𝒍𝒐𝒈 𝒏).
Example 1:
Let us consider that the capacity of the knapsack 𝑾 = 𝟔𝟎 and the list of provided item are
shown in below
Profits=(p1,p2,p3,p4)=(280,100,120,120)
Weigts=(w1,w2,w3,w4)=(40,10,20,24)
As the provided items are not sorted based on pi / wi. After sorting, the items are as
shown in the following table.
ITEM B A C D
WEIGHT 10 40 20 24
RATIO 10 7 6 5
(Pi/Wi)
After sorting all the items according to 𝒑𝒊/𝒘𝒊. First all of B is chosen as 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.
Now, 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.
import java.util.*;
class Greedy_Knapsack
{
public static void main(String args[])
{
int m,i,j,k[],temp1;
float max,temp;
float w[],p[],r[],x[],n,w1=0,sum=0;
Scanner sc=new Scanner(System.in);
System.out.println("enter kanpsack size ");
n=sc.nextFloat();
System.out.println("enter the no.of Items ");
m=sc.nextInt();
w=new float[m];
p=new float[m];
r=new float[m];
x=new float[m];
k=new int[m];
System.out.println("enter the weigths of Items ");
for(i=0;i<m;i++)
{
k[i]=i+1;
w[i]=sc.nextInt();
}
System.out.println("enter the profits of Items");
for(i=0;i<m;i++)
{
p[i]=sc.nextInt();
}
for(i=0;i<m;i++)
{
r[i]=p[i]/w[i];
}
for(i=0;i<m-1;i++)
{
for(j=0;j<m-i-1;j++)
{
if(r[j+1]>r[j])
{
temp=r[j];
r[j]=r[j+1];
r[j+1]=temp;
temp=p[j];
p[j]=p[j+1];
p[j+1]=temp;
temp=w[j];
w[j]=w[j+1];
w[j+1]=temp;
temp1=k[j];
k[j]=k[j+1];
k[j+1]=temp1;
}
}
}
for(i=0;i<m;i++)
{
if(n>w[i]) {
n=n-w[i];
x[i]=1;
}
else if(n==0){
x[i]=0;
}
else
{
x[i]=n/w[i];
n=0;
}
}
System.out.println("feasible solution is ");
System.out.println("Itemno"+"\t"+"Weights"+"\t"+"Profits"+"\t"+"pi/wi Ratio"+"\t"
+"Selected");
for(i=0;i<m;i++)
{
System.out.print(k[i]+"\t"+w[i]+"\t"+p[i]+"\t"+r[i]+"\t"+"\t"+ x[i]+"\n");
}
for(i=0;i<m;i++)
{
sum=sum+(x[i]*p[i]);
}
Output:
enter kanpsack size
60
enter the no.of Items
4
enter the weigths of Items
40 10 20 24
enter the profits of Items
280 100 120 120
feasible solution is
Itemno Weights Profits pi/wi Ratio Selected
2 10.0 100.0 10.0 1.0
1 40.0 280.0 7.0 1.0
3 20.0 120.0 6.0 0.5
4 24.0 120.0 5.0 0.0
Optimal Solution: Maximum Profit is
440.0
Disjoint Sets:
A disjoint-set data structure is a data structure that keeps track of a set of elements
partitioned into a number of disjoint (non-overlapping) subsets.
A union-find algorithm is an algorithm that performs two useful operations on such a data
structure:
Find: Determine which subset a particular element is in. This can be used for
determining if two elements are in the same subset.
In this post, we will discuss the application of Disjoint Set Data Structure.
The application is to check whether a given graph contains a cycle or not.
Union-Find Algorithm can be used to check whether an undirected graph contains cycle
or not. Note that we have discussed an algorithm to detect cycle.
This is another method based on Union-Find. This method assumes that the graph doesn’t
contain any self-loops.
Two sets A and B are said to be disjoint if there are no common elements
i.e., A B = .
Example:
S1={1,7,8,9}, S2={2,5,10}, and S3={3,4,6} are three disjoint sets.
Pseudo code
Array-Representation:
• If j is a node on the path from i to its root and parent[i]≠ root(i), then set parent[j]
to root(i).
•
The first run of find operation will collapse the tree. Therefore, all following find operation
of the same element only goes up one link to find the root.
Pseudo code :
4.1.Kruskal’s Algorithm:
Step 1 - Remove all loops and Parallel Edges.
Step 2 - Arrange all edges in their increasing order of weight.
Step 3 - Add the edge which has the least weightage iff it does not form cycle.
Pseudo Code:
Time Complexity: O(ElogE)+O(E*4*alpha), ElogE for sorting and E*4*alpha for findParent
operation ‘E’ times
import java.util.*;
class Node
{
private int u;
private int v;
private int weight;
}
}
class Kruskals_Algo
{
private int findPar(int u, int parent[])
{
if(u==parent[u]) return u;
return parent[u] = findPar(parent[u], parent);
}
private void union(int u, int v, int parent[], int rank[])
{
u = findPar(u, parent);
v = findPar(v, parent);
if(rank[u] < rank[v])
{
parent[u] = v;
}
else if(rank[v] < rank[u])
{
parent[v] = u;
}
else
{
parent[v] = u;
rank[u]++;
}
}
void KruskalAlgo(ArrayList<Node> adj, int N)
{
Collections.sort(adj, new SortComparator());
int parent[] = new int[N];
int rank[] = new int[N];
for(int i = 0;i<N;i++)
{
parent[i] = i;
rank[i] = 0;
}
int costMst = 0;
ArrayList<Node> mst = new ArrayList<Node>();
for(Node it: adj)
{
if(findPar(it.getU(), parent) != findPar(it.getV(), parent))
{
costMst += it.getWeight();
mst.add(it);
union(it.getU(), it.getV(), parent, rank);
}
}
System.out.println("minimum cost is:"+costMst);
System.out.println("Spanning Tree is");
for(Node it: mst)
{
System.out.println(it.getU() + " - " +it.getV());
}
}
public static void main(String args[])
{
Scanner sc = new Scanner(System.in);
}
}
input=
Enter number of vertices:
5
Enter weight for edge
0
Enter source value for edge
1
Enter destination value for edge
2
Enter weight for edge
0
Enter source value for edge
3
Enter destination value for edge
6
Enter weight for edge
1
Enter source value for edge
3
Enter destination value for edge
8
Enter weight for edge
1
Enter source value for edge
2
Enter destination value for edge
3
Enter weight for edge
1
Enter source value for edge
4
Enter destination value for edge
5
output=
minimum cost is:16
Spanning Tree is
0-1
1-2
1-4
0-3
4.2.Prim’s Algorithm:
Prim's algorithm, in contrast with Kruskal's algorithm, treats the nodes as a single
tree and keeps on adding new nodes to the spanning tree from the given graph.
Step 1 - Remove all loops and parallel edges.
Step 2 - Choose any arbitrary node as root node.
Step 3 - Check outgoing edges and select the one with less cost.
Pseudo Code:
import java.util.*;
class Prims_Algo
{
public void Prim(int G[][], int V)
{
int INF = 9999999;
// create a array to track selected vertex // selected will become true otherwise false
if (selected[i] == true) {
for (int j = 0; j < V; j++) {
// not in selected and there is an edge
if (!selected[j] && G[i][j] != 0) {
if (min > G[i][j]) {
min = G[i][j];
x = i;
y = j;
}
}
}
}
}
System.out.println(x + " - " + y + " : " + G[x][y]);
selected[y] = true;
no_edge++;
}
}
public static void main(String[] args)
{
Test g = new Test();
Scanner sc=new Scanner(System.in);
System.out.println("enter number vertices");
int V=sc.nextInt();
System.out.println("enter row size of the matrix");
int x=sc.nextInt();
System.out.println("enter column size of the matrix");
int y=sc.nextInt();
g.Prim(G, V);
}
}
input=
enter number vertices
5
enter row size of the matrix
5
enter column size of the matrix
5
adjacency matrix is
0 9 75 0 0
9 0 95 19 42
75 95 0 51 66
0 19 51 0 31
0 42 66 31 0
output=
Edge : Weight
0-1: 9
1 - 3 : 19
3 - 4 : 31
3 - 2 : 51
Dijkstra's Algorithm requires a graph and source vertex to work.The algorithm is purely based on
greedy approach and thus finds the locally optimal choice(local minima in this case) at each step
of the algorithm.
In this algorithm each vertex will have two properties defined for it-
Visited property:-
o This property represents whether the vertex has been visited or not.
o We are using this property so that we don't revisit a vertex.
o A vertex is marked visited only after the shortest path to it has been found.
Path property:-
o This property stores the value of the current minimum path to the vertex. Current
minimum path means the shortest way in which we have reached this vertex till
now.
o This property is updated whenever any neighbour of the vertex is visited.
o The path property is important as it will store the final answer for each vertex.
Initially all the vertices are marked unvisited as we have not visited any of them. Path to all the
vertices is set to infinity excluding the source vertex. Path to the source vertex is set to zero(0).
Then we pick the source vertex and mark it visited.After that all the neighbours of the source
vertex are accessed and relaxation is performed on each vertex. Relaxation is the process of
trying to lower the cost of reaching a vertex using another vertex.
In relaxation, the path of each vertex is updated to the minimum value amongst the current path
of the node and the sum of the path to the previous node and the path from the previous node to
this node.
Assume that p[v] is the current path value for node v, p[n] is the path value upto the previously
visited node n, and w is the weight of the edge between the curent node and previously visited
node(edge weight between v and n)
Then in every subsequent step, an unvisited vertex with the least path value is marked visited and
its neighbour's paths updated.
The above process is repeated till all the vertices in the graph are marked visited.
Whenever a vertex is added to the visited set, the path to all of its neighbouring vertices is
changed according to it.
If any of the vertex is not reachable(disconnected component), its path remains infinity. If the
source itself is a disconected component, then the path to all other vertices remains infinity.
Pseudo Code:
Dijkstra’s_Shortestpath_algo.java
import java.util.*;
import java.io.*;
import java.lang.*;
import java.util.*;
class Dijkstra’s_Shortestpath_algo
{
// A utility function to find the vertex with minimum distance value, from the set of
// vertices not yet included in shortest path tree
return min_index;
}
// Function that implements Dijkstra's single source shortest path algorithm for a graph
represented using adjacency matrix representation
// Update dist[v] only if is not in sptSet, there is an edge from u to v, and total
// weight of path from src to v through u is smaller than current value of dist[v]
// Function call
t.dijkstra(graph, 0);
}
}
Input:
040000080
4 0 8 0 0 0 0 11 0
080704002
0 0 7 0 9 14 0 0 0
0 0 0 9 0 10 0 0 0
0 0 4 14 10 0 2 0 0
000002016
8 11 0 0 0 0 1 0 7
002000670
Output:
Vertex Distance from Source
0 0
1 4
2 12
3 19
4 21
5 11
6 9
7 8
8 14
General method:
There are many problems that can be solved by the use of a backtracking algorithm, and
it can be used over a complex set of variables or constraints, they are basically of two
types :
1. Implicit constraint: a particular rule used to check how many each element is in a
proper sequence is related to each other.
2. Explicit constraint: the rule that restricts every element to get chosen from the
particular set.
Applications:
1. N Queens Problem.
2. Hamiltonian Cycle.
3. Brace Expansion.
4. Gray Code.
5. Path with Maximum Gold.
6. Generalized Abbreviation.
7. Campus Bikes II.
1. N Queens Problem:
N Queen problem is the classical Example of backtracking. N-Queen problem is defined as,
“given N x N chess board, arrange N queens in such a way that no two queens attack each other
by being in same row, column or diagonal”.
For N = 1, this is trivial case. For N = 2 and N = 3, solution is not possible. So we start
with N = 4 and we will generalize it for N queens.
Problem : Given 4 x 4 chessboard, arrange four queens in a way, such that no two
queens attack each other. That is, no two queens are placed in the same row, column, or
diagonal.
We have to arrange four queens, Q1, Q2, Q3 and Q4 in 4 x 4 chess board. We will put
ith queen in ith row. Let us start with position (1, 1). Q1 is the only queen, so there is no
issue. partial solution is <1>
We cannot place Q2 at positions (2, 1) or (2, 2). Position (2, 3) is acceptable. partial
solution is <1, 3>.
Next, Q3 cannot be placed in position (3, 1) as Q1 attacks her. And it cannot be placed at
(3, 2), (3, 3) or (3, 4) as Q2 attacks her. There is no way to put Q3 in third row. Hence,
the algorithm backtracks and goes back to the previous solution and readjusts the position
of queen Q2. Q2 is moved from positions (2, 3) to
(2, 4). Partial solution is <1, 4>
Now, Q3 can be placed at position (3, 2). Partial solution is <1, 4, 3>.
Queen Q4 cannot be placed anywhere in row four. So again, backtrack to the previous
solution and readjust the position of Q3. Q3 cannot be placed on (3, 3) or(3, 4). So the
algorithm backtracks even further.
All possible choices for Q2 are already explored, hence the algorithm goes back to partial
solution <1> and moves the queen Q1 from (1, 1) to (1, 2). And this process continues
until a solution is found.
All possible solutions for 4-queen are shown in fig (a) & fig. (b)
We can see that backtracking is a simple brute force approach, but it applies some
intelligence to cut out unnecessary computation. The solution tree for the 4-queen
problem is shown in Fig. (7.2)
Fig. (7.6) describes the backtracking sequence for the 4-queen problem.
The solution of the 4-queen problem can be seen as four tuples (x 1, x2, x3, x4),
where xi represents the column number of queen Qi. Two possible solutions for
the 4-queen problem are (2, 4, 1, 3) and (3, 1, 4, 2).
TimeComplexity: O(N!)
Auxiliary Space: O(N2)
import java.util.*;
return true;
}
board[i][col] = 0;
}
}
return false;
}
boolean solveNQ()
{
int board[][] = new int[N][N];
if (solveNQUtil(board, 0) == false)
{
System.out.print("No Solution");
return false;
}
printSolution(board);
return true;
}
case =1
input =1
output ="1"
case =2
input =3
output =""
case =3
input =4
output ="0100
0001
1000
0010"
output ="0010
1000
0001
0100"
2. Hamiltonian Cycle:
The Hamiltonian cycle is the cycle in the graph which visits all the vertices in graph exactly
once and terminates at the starting node. It may not include all the edges
The Hamiltonian cycle problem is the problem of finding a Hamiltonian cycle in a graph if
there exists any such cycle.
The input to the problem is an undirected, connected graph. For the graph shown in Figure
(a), a path A – B – C – D – E – F—G—H—A forms a Hamiltonian cycle. It visits all the
vertices exactly once.
The Hamiltonian cycle problem is also both, decision problem and an optimization
problem. A decision problem is stated as, “Given a path, is it a Hamiltonian cycle of the
graph?”.
The optimization problem is stated as, “Given graph G, find the Hamiltonian cycle for the
graph.”
We can define the constraint for the Hamiltonian cycle problem as follows:
In any path, vertex i and (i + 1) must be adjacent.
1st and (n – 1)th vertex must be adjacent (nth of cycle is the initial vertex itself).
Vertex i must not appear in the first (i – 1) vertices of any path.
With the adjacency matrix representation of the graph, the adjacency of two vertices can be
verified in constant time.
Complexity Analysis:
Looking at the state space graph, in worst case, total number of nodes in tree would be,
T(n) = O(nn). Thus, the Hamiltonian cycle algorithm runs in exponential time.
Example: Consider a graph G = (V, E) shown in fig. we have to find a Hamiltonian circuit using
Backtracking method.
Firstly, we start our search with vertex 'a.' this vertex 'a' becomes the root of our implicit
tree.
Next, we choose vertex 'b' adjacent to 'a' as it comes first in lexicographical order (b, c, d).
Next, we select vertex 'f' adjacent to 'e.' The vertex adjacent to 'f' is d and e, but they have already
visited. Thus, we get the dead end, and we backtrack one step and remove the vertex 'f' from
partial solution.
From backtracking, the vertex adjacent to 'e' is b, c, d, and f from which vertex 'f' has already
been checked, and b, c, d have already visited. So, again we backtrack one step. Now, the vertex
adjacent to d are e, f from which e has already been checked, and adjacent of 'f' are d and e. If 'e'
vertex, revisited them we get a dead state. So again we backtrack one step.
Now, adjacent to c is 'e' and adjacent to 'e' is 'f' and adjacent to 'f' is 'd' and adjacent to 'd' is 'a.'
Here, we get the Hamiltonian Cycle as all the vertex other than the start vertex 'a' is visited only
once. (a - b - c - e - f -d - a).
Again Backtrack
Here we have generated one Hamiltonian circuit, but another Hamiltonian circuit can
also be obtained by considering another vertex.
KMIT Page no: 82
DESIGN AND ANALYSIS OF ALGORITHMS UNIT-II
import java.util.*;
class HamiltonianCycle
{
static int V ;
int path[];
return true;
}
path[pos] = -1;
}
}
return false;
}
path[0] = 0;
if (hamCycleUtil(graph, path, 1) == false)
{
System.out.println("\nNo Solution");
return 0;
}
printSolution(path);
return 1;
}
Sample Input-1:
---------------
5
01010
10111
01001
11001
01110
Sample Output-1:
----------------
0 1 2 4 3 0
Sample Input-2:
---------------
5
01010
10111
01001
11000
01100
Sample Output-2:
----------------
No Solution
3. Brace Expansion:
Under the grammar given below, strings can represent a set of lowercase words.
Let R(expr) denote the set of words the expression represents.
Given an expression representing a set of words under the given grammar, return the sorted list
of words that the expression represents.
Example 1:
Output: ["ac","ad","ae","bc","bd","be"]
Example 2:
Output: ["a","ab","ac","z"]
Explanation: Each distinct word is written only once in the final answer.
Constraints:
import java.util.*;
char c = s.charAt(index);
int position = sb.length();
if (c == '[') {
List<Character> charList = new ArrayList<>();
int endIndex = index + 1;
while (endIndex < s.length() && s.charAt(endIndex) != ']') {
if (Character.isLetter(s.charAt(endIndex))) { charList.add(s.charAt(endIndex));
}
endIndex++;
}
Collections.sort(charList);
for (char d : charList) {
sb.append(d);
dfs(s, endIndex + 1, sb, res);
sb.setLength(position);
}
} else if (Character.isLetter(c)) {
sb.append(s.charAt(index));
dfs(s, index + 1, sb, res);
}
}
public static void main(String args[] )
{
Scanner sc = new Scanner(System.in);
String str=sc.next();
System.out.println(Arrays.deepToString(expand(str)));
}
}
case =1
input =[a,b,c,d]e[x,y,z]
output =[aex, aey, aez, bex, bey, bez, cex, cey, cez, dex, dey, dez]
case =2
input =[ab,cd]x[y,z]
output =[abxy, abxz, cdxy, cdxz]
4. Gray Code:
Example 1:
Input: n = 2
Output: [0,1,3,2]
Explanation:
[0,2,3,1] is also a valid gray code sequence, whose binary representation is [00,10,11,01].
Example 2:
Input: n = 1
Output: [0,1]
import java.util.*;
class GrayCode
{
int nums = 0;
public List<Integer> grayCode(int n)
{
List<Integer> ret = new ArrayList<>();
backTrack(n, ret);
return ret;
}
}
public static void main( String args[])
{
Scanner sc=new Scanner(System.in);
int N=sc.nextInt();
System.out.println(new GrayCode().grayCode(N));
}
}
Output:
case =1
input =2
output =[0, 1, 3, 2]
case =2
input =3
output =[0, 1, 3, 2, 6, 7, 5, 4]
In a gold mine grid of size m x n, each cell in this mine has an integer representing the amount of
gold in that cell, 0 if it is empty.
Return the maximum amount of gold you can collect under the conditions:
Every time you are located in a cell you will collect all the gold in that cell.
From your position, you can walk one step to the left, right, up, or down.
You can't visit the same cell more than once.
Never visit a cell with 0 gold.
You can start and stop collecting gold from any position in the grid that has some gold.
Example 1:
Output: 24
Explanation:
[[0,6,0],
[5,8,7],
[0,9,0]]
Example 2:
Output: 28
Explanation:
[[1,0,7],
[2,0,6],
[3,4,5],
[0,3,0],
[9,0,20]]
Path to get the maximum gold, 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7.
Constraints:
m == grid.length
n == grid[i].length
1 <= m, n <= 15
0 <= grid[i][j] <= 100
There are at most 25 cells containing gold.
Time Complexity: O(n x m) where n is the number of rows and m is the number of columns of
the given input matrix.
Each cell in this matrix has an amount of gold present at it. We have to start digging from
the 0th column and reach the last column and in the procedure, we can move either
diagonally one step upward (called "d1") or one step forward in the same row (called
"d2") or diagonally one step downward (called "d3") collecting the gold at the cell we
reach. So, we have to find the paths with the maximum gold.
For instance, in the matrix shown above, the maximum gold that we can collect is 18 and
there are 3 different paths to reach 18 as shown in the image below:
As you can see, the maximum amount of gold is 18. The first path is "2 d1 d2 d3". This
means that we are starting from the 2nd row and the path is "d1 d2 d3".
We have already discussed the meaning of "d1", "d2" and "d3". Why are we mentioning
only the starting row and not the starting column in the paths? This is because we have
already been told in the question that we have to start from the 0th column only.
import java.util.*;
class GetMaximumGold
{
public int getMaximumGold(int[][] grid)
{
int maxGold = 0;
for (int i = 0; i < grid.length; i++) {
for (int j = 0; j < grid[0].length; j++) {
maxGold = Math.max(maxGold, getMaximumGoldBacktrack(grid, i, j, 0));
}
}
return maxGold;
}
grid[i][j] = 0;
maxGold = Math.max(maxGold, getMaximumGoldBacktrack(grid, i+1, j,
curGold));
maxGold = Math.max(maxGold, getMaximumGoldBacktrack(grid, i, j+1,
curGold));
maxGold = Math.max(maxGold, getMaximumGoldBacktrack(grid, i-1, j, curGold));
maxGold = Math.max(maxGold, getMaximumGoldBacktrack(grid, i, j-1, curGold));
grid[i][j] = temp;
return maxGold;
}
Output:
input =3 3
060
587
090
output =24
case =2
input =5 3
107
206
345
030
9 0 20
output =28
6. Generalized Abbreviation:
Example:
Input: "word"
Output:
["word", "1ord", "w1rd", "wo1d", "wor1", "2rd", "w2d", "wo2", "1o1d", "1or1", "w1r1",
"1o2", "2r1", "3d", "w3", "4"]
For example:
Sample Input-> pep
Sample Output-> pep pe1 p1p p2 1ep 1e1 2p 3 (in different lines)
HOW?
First of all, generate all the binaries of the length equal to the length of the input string.
Binaries -
Approach:
We can observe that whenever the bit is OFF (zero), we will use the character of the
string at that position and whenever the bit is ON (one), we will count all the ON (one)
bits that are together and then replace them with the count of the ON (one) bits.
The number of abbreviations will be equal to 2^n, where n = length of the string (as the
number of binaries with given n is equal to 2^n ).
In the above example n = 3, therefore the number of abbreviations will be 8 (2^3 = 8).
We can see that this question is like the subsequence problem, we must maintain one
more variable which will count the characters that were not included in the subsequence.
Let's try to draw the recursion tree diagram for the word, "pep". In recursion
diagram, (a | b | c) a represents the test string, b represents the answer so far
and c represents the count.
In the above diagram, (pep|.|0) has two options, either it can include the first p in the answer
or not. If it includes the p in the answer then p is concatenated in the answer so far string
and count remains 0.
Then at (pep|p|0), we further explore the options for character e. It has two options, either it
can include e in the answer or not. If it includes e in the answer then e is concatenated in the
answer so far string and count remains 0.
Then at (pep|pe|0), we further explore the options for character p (second p). It has two
options, either it can include p in the answer or not. If it includes p in the answer then p is
concatenated in the answer so far string and count remains 0.
Since we have reached the end of the string, and the count is 0, therefore, the asf i.e.
pep is the first possible output.
Then we come back at (pep|pe|0) and explore the path if the second p is not added in the asf.
In this case, p will not be added in psf and therefore count will become 1.
And again, we have reached the end of the string, but count is 1 this time, therefore,
this count will be concatenated at the end of the asf, which will give us the next possible
output, pe1.
We have explored both the possibilities via (pep|pe|0) therefore, we go back at (pep|p|0), and
see what happens if e was not added to asf.
First of all count becomes 1, and we reach (pep|p|1).
Then at (pep|p|1), we further explore the options for character p (second p). It has two
options, either it can include p in the answer or not. If p is to be included in the answer then
we see that our previous count is not 0, so we concatenate the previous count in the asf and
then second p.
Since we have reached the end of the string, and the count is 0, therefore, the asf i.e.
p1p is the next possible output.
Then we come back at (pep|p|1) and explore the path if the second p is not added in the asf.
In this case, p will not be added in psf and therefore count will become 2.
And again, we have reached the end of the string, but count is 2 this time, therefore,
this count will be concatenated at the end of the asf, which will give us the next possible
output, p2.
We have explored both the possibilities via (pep|p|1) therefore; we go back at (pep|p|0).
(pep|p|0) has also explored both the options, if "e" were added or not) therefore; we go back
at (pep|.|0).
At (pep|.|0), now we explore the possibilities of, if first p were not added in the asf.
If "p" is not added in the answer so far then count becomes 1.
Then at (pep|.|1), we further explore the options for character e. It has two options, either it
can include e in the answer or not. If it includes e in the answer then e is concatenated with
the current count in the answer so far string and count is set to 0.
Then at (pep|1e|0), we further explore the options for character p (second p).
It has two options, either it can include p in the answer or not. If it includes p in the answer
then p is concatenated in the answer so far string and count remains 0.
Since we have reached the end of the string, and the count is 0, therefore, the asf i.e. 1ep is
the next possible output.
Then we come back at (pep|1e|0) and explore the path if the second p is not added in the asf.
In this case, p will not be added in psf and therefore count will become 1.
And again, we have reached the end of the string, but count is 1 this time, therefore,
this count will be concatenated at the end of the asf, which will give us the next possible
output, 1e1.
We have explored both the possibilities via (pep|1e|0) therefore, we go back at (pep|.|1), and
see what happens if e was not added to asf.
Then at (pep|.|2), we further explore the options for character p (second p). It has two
options, either it can include p in the answer or not.
If p is to be included in the answer then we see that our previous count is not 0, so we
concatenate the previous count (2) in the asf and then second p.
Since we have reached the end of the string, and the count is 0, therefore, the asf i.e. 2p
is the next possible output.
Then we come back at (pep|.|2) and explore the path if the second p is not added in the asf.
In this case, p will not be added in psf and therefore count will become 3.
And again, we have reached the end of the string, but count is 3 this time, therefore,
this count will be concatenated at the end of the asf (empty at this stage), which will
give us the next possible output, 3.
We have explored both the possibilities via (pep|.|2) therefore; we go back at (pep|.|1).
(pep|.|1) has also explored both the options, if "e" were added or not) therefore; we go back
at (pep|.|0).
At (pep|.|0), we have explored both the possibilities that is if first p were not added in the
asf.
import java.util.*;
class GenerateAbbreviations
{
public List<String> makeShortcutWords(String word)
{
List<String> ret = new ArrayList<String>();
backtrack(ret, word, 0, "", 0);
Collections.sort(ret);
return ret;
}
private void backtrack(List<String> ret, String word, int pos, String cur, int count)
{
if(pos==word.length())
{
if(count > 0) cur += count;
ret.add(cur);
}
else{
backtrack(ret, word, pos + 1, cur, count + 1);
backtrack(ret, word, pos+1, cur + (count>0 ? count : "") + word.charAt(pos), 0);
}
}
case =1
input =kmit
output =[1m1t, 1m2, 1mi1, 1mit, 2i1, 2it, 3t, 4, k1i1, k1it, k2t, k3, km1t, km2, kmi1, kmit]
case =2
input =cse
output =[1s1, 1se, 2e, 3, c1e, c2, cs1, cse]
case =3
input =elite
output =[1l1t1, 1l1te, 1l2e, 1l3, 1li1e, 1li2, 1lit1, 1lite, 2i1e, 2i2, 2it1, 2ite, 3t1, 3te, 4e, 5,
e1i1e, e1i2, e1it1, e1ite, e2t1, e2te, e3e, e4, el1t1, el1te, el2e, el3, eli1e, eli2, elit1, elite]
case =4
input =r
output =[1, r]
Solution:
To solve this, we will follow these steps −
Define a function helper(). This will take a,b
o return |a[0]-b[0]| + |a[1] - b[1]|
Define a function solve(). This will take bikes, workers,bikev,i:= 0
info := a list with i and bikev
if info is present in memo, then
o return memo[info]
if i is same as size of workers, then
o return 0
temp := infinity
for j in range 0 to size of bikes, do
o if not bikev[j] is non-zero, then
bikev[j]:= 1
temp := minimum of temp, helper(workers[i], bikes[j]) +solve(bikes,
workers, bikev, i+1)
KMIT Page no:
103
DESIGN AND ANALYSIS OF ALGORITHMS UNIT-II
bikev[j]:= 0
memo[info]:= temp
return temp
Define a function assignBikes(). This will take workers, bikes
bikev := a list whose size is same as the size of bikes, fill this with false
memo:= a new map
return solve(bikes, workers, bikev)
Java program for Campus Bikes-II: CampusBikes.java
import java.util.*;
class CampusBikes
{
int min = Integer.MAX_VALUE;
public int assignBikes(int[][] workers, int[][] bikes)
{
backtrack(new boolean[bikes.length],0,workers,bikes,0);
return min;
}
void backtrack(boolean[] visited, int i, int[][] workers, int[][] bikes, int distance)
{
if (i==workers.length && distance < min) min = distance;
if (i>=workers.length) return;
if (distance>min) return;
for (int j=0; j<bikes.length; j++){
if (visited[j]) continue;
visited[j] = true;
backtrack(visited, i+1, workers, bikes, distance+dist(i,j,workers,bikes));
visited[j] = false;
}
}
Sample Output-1:
----------------
17
Sample Input-2:
---------------
22 //No of workers and vehicles
0 0 // co-ordinates of workers
21
1 2 // co-ordinates of vehicles
33
Sample Output-2:
6