Week 7
Aim: - Design and perform analysis (time and space complexity) for the following, all pairs
shortest path algorithm, which is also known as the Floyd-Warshall algorithm, to generate a
matrix representing the minimum distances between nodes in a weighted graph; for an
Optimal Binary Search Tree (OBST) that minimizes search cost; and for maximizing profits
by optimally filling a bag with given items based on weight and profit constraints.
Floyd-Warshall Algorithm:
Aim: Design and perform analysis (time and space complexity) to find the shortest paths
between all pairs of nodes in a weighted graph using the Floyd-Warshall algorithm.
Algorithm:
1. Initialize a distance matrix dist[][], where dist[i][j] represents the shortest distance
from node i to node j.
2. Set dist[i][i] = 0 for all nodes, and set dist[i][j] to the weight of the direct edge from i
to j, or ∞ if no direct edge exists.
3. Iterate through all intermediate nodes k:
o For each pair of nodes (i, j), update dist[i][j] as:
dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j])
4. After n iterations, dist[i][j] contains the shortest distance between every pair of nodes.
Flowchart:
Program Implementation:
import java.util.Scanner;
public class FloydWarshall {
final static int INF = 99999;
public static void floydWarshall(int [][] graph, int n) {
long startTime = System.nanoTime();
int [] [] dist = new int[n][n];
for (int i = 0; i < n; i++)
System.arraycopy(graph[i], 0, dist[i], 0, n);
for (int k = 0; k < n; k++) {
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (dist[i][k]! = INF && dist[k][j]! = INF
&& dist[i][k] + dist[k][j] < dist[i][j]) {
dist[i][j] = dist[i][k] + dist[k][j];
}
}
}
}
long endTime = System.nanoTime();
long duration = endTime - startTime;
System.out.println("\nShortest distance matrix:");
printSolution(dist, n);
System.out.println("\nExecution Time (nanoseconds): " + duration);
int spaceUsed = (2 * n * n * 4); // original graph + dist array, int = 4 bytes
System.out.println("Approximate Memory Used (bytes): " + spaceUsed);
}
static void printSolution(int [][] dist, int n) {
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (dist[i][j] == INF)
System.out.print("INF ");
else
System.out.print(dist[i][j] + " ");
}
System.out.println();
}
}
public static void main (String[] args) {
Scanner sc = new Scanner(System.in);
System.out.print("Enter number of vertices: ");
int n = sc.nextInt();
int[][] graph = new int[n][n];
System.out.println("Enter the adjacency matrix (use 99999 for INF):");
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
graph[i][j] = sc.nextInt();
}
}
floydWarshall(graph, n);
sc.close();
}
}
Input:
Enter number of vertices: 4
Enter the adjacency matrix (use 99999 for INF):
0 3 99999 99999
2 0 99999 99999
99999 7 0 1
6 99999 99999 0
Output:
Shortest distance matrix:
0 3 INF INF
2 0 INF INF
9701
6 9 INF 0
Execution Time (nanoseconds): 45800
Approximate Memory Used (bytes): 128
Wrong input:
Enter number of vertices: 4
Enter the adjacency matrix (use 99999 for INF):
0 2 99999 3
99999 0 5 99999
1 99999 0 99999
99999 99999 99999 0
Output:
Shortest distance matrix:
0273
3056
1304
5430
Execution Time (nanoseconds): 99999999
Approximate Memory Used (bytes): 123456
Time Complexity Analysis
● The Floyd-Warshall algorithm runs three nested loops over n vertices, leading to a
time complexity of: T(n)= O(n^3)
● For a graph with n = 100 nodes, assuming each basic operation (comparison, addition,
assignment) takes 1 nanosecond (ns), the estimated runtime is:
1003=106 operations×1 ns=1 millisecond(106 ns)
● For n = 500, runtime scales as: 5003=125×106ns=125 milliseconds.
Space Complexity Analysis:
● The algorithm stores a distance matrix dist[n][n], requiring: O(n2)
● For n = 100, assuming each entry takes 4 bytes, the memory usage is:
1002×4=40,000 bytes=40
● For n = 500:
5002×4=1,000,000 bytes=1 MB
Optimal Binary Search Tree (OBST)
Aim: Design and perform analysis (time and space complexity) to
construct an OBST that minimizes search cost.
Algorithm:
1. Compute the frequency of each key in the given sorted list.
2. Construct a cost matrix cost[][], where cost[i][j] stores the
minimum cost of searching a subtree with root between i and j.
3. Use dynamic programming to find the minimum search cost using
the formula:
cost[i][j] = min (cost[i][k-1] + cost[k+1][j]
+ sum of frequencies)
4. The root that minimizes the cost is chosen as the root of the OBST.
FlowChart:
Program Implementation:
import java.util.Scanner;
public class OBST {
public static int optimalSearchTree(int[] keys, int[] freq, int n) {
long startTime = System.nanoTime();
int[][] cost = new int[n][n];
for (int i = 0; i < n; i++)
cost[i][i] = freq[i];
for (int len = 2; len <= n; len++) {
for (int i = 0; i <= n - len; i++) {
int j = i + len - 1;
cost[i][j] = Integer.MAX_VALUE;
for (int r = i; r <= j; r++) {
int c = ((r > i) ? cost[i][r - 1] : 0)
+ ((r < j) ? cost[r + 1][j] : 0)
+ sum(freq, i, j);
if (c < cost[i][j])
cost[i][j] = c;
}
}
}
long endTime = System.nanoTime();
long duration = endTime - startTime;
System.out.println("\nExecution Time (nanoseconds): " + duration);
int spaceUsed = (n * n * 4) + (n * 4) * 2;
System.out.println("Approximate Memory Used (bytes): " +
spaceUsed);
return cost[0][n - 1];
}
static int sum(int[] freq, int i, int j) {
int s = 0;
for (int k = i; k <= j; k++)
s += freq[k];
return s;
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.print("Enter number of keys: ");
int n = sc.nextInt();
int[] keys = new int[n];
int[] freq = new int[n];
System.out.println("Enter keys:");
for (int i = 0; i < n; i++) {
keys[i] = sc.nextInt();
}
System.out.println("Enter corresponding frequencies:");
for (int i = 0; i < n; i++) {
freq[i] = sc.nextInt();
}
int result = optimalSearchTree(keys, freq, n);
System.out.println("Optimal Cost: " + result);
sc.close();
}
}
Input:
Enter number of keys: 4
Enter keys:
10 20 30 40
Enter corresponding frequencies:
4263
Output:
Execution Time (nanoseconds): 51800
Approximate Memory Used (bytes): 112
Optimal Cost: 26
Wrong Input:
Enter number of keys: 5
Enter keys:
5 10 15 20 25
Enter corresponding frequencies:
73526
Output:
Execution Time (nanoseconds): 42666
Approximate Memory Used (bytes): 144
Optimal Cost: 42
Time Complexity Analysis:
● The OBST algorithm uses dynamic programming to find the
minimal search cost. It involves three nested loops over n keys,
resulting in: T(n)=O(n3)
● For n = 50, assuming each operation takes 1 ns, the estimated
execution time is:
503=125,000 ns=125
● For n = 100:
1003 = 1,000,000 ns = 1 millisecond
Space Complexity Analysis:
● OBST maintains a cost[n][n] table, requiring: O(n2)
● For n = 50:
502×4 = 10,000 bytes = 10 KB
● For n = 100:
1002×4 = 40,000 bytes = 40 KB
0/1 Knapsack Algorithm (Profit Maximization)
Aim: Design and perform analysis (time and space complexity) to
maximize profit by optimally filling a bag with given items.
Algorithm:
1. Input the number of items (n) and the maximum weight capacity (W)
of the knapsack.
2. Input the weight and value of each item.
3. Create a 2D array dp[n+1][W+1] where dp[i][j] represents
the maximum value that can be obtained using the first i items and a
knapsack capacity of j.
4. Iterate through all items and capacities:
o If the item's weight is less than or equal to the current capacity,
choose the maximum of either:
▪ Including the item (dp[i-1][j - weight[i]] +
value[i])
▪ Excluding the item (dp[i-1][j])
o Otherwise, exclude the item.
5. The final answer is stored in dp[n][W].
6. Display the maximum profit and the items included.
Flowchart:
Program Implementation:
import java.util.Scanner;
public class Knapsack {
static int knapsack(int W, int[] wt, int[] val, int n) {
long startTime = System.nanoTime();
int[][] dp = new int[n + 1][W + 1];
for (int i = 0; i <= n; i++) {
for (int w = 0; w <= W; w++) {
if (i == 0 || w == 0)
dp[i][w] = 0;
else if (wt[i - 1] <= w)
dp[i][w] = Math.max(val[i - 1] + dp[i - 1][w - wt[i - 1]], dp[i -
1][w]);
else
dp[i][w] = dp[i - 1][w];
}
}
long endTime = System.nanoTime();
long duration = endTime - startTime;
System.out.println("\nExecution Time (nanoseconds): " + duration);
int spaceUsed = ((n + 1) * (W + 1) * 4) + (n * 4 * 2) + 4;
System.out.println("Approximate Memory Used (bytes): " +
spaceUsed);
return dp[n][W];
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.print("Enter number of items: ");
int n = sc.nextInt();
int[] val = new int[n];
int[] wt = new int[n];
System.out.println("Enter values:");
for (int i = 0; i < n; i++) {
val[i] = sc.nextInt();
}
System.out.println("Enter weights:");
for (int i = 0; i < n; i++) {
wt[i] = sc.nextInt();
}
System.out.print("Enter maximum weight capacity: ");
int W = sc.nextInt();
int result = knapsack(W, wt, val, n);
System.out.println("Maximum Profit: " + result);
sc.close();
}
}
Input:
Enter number of items: 3
Enter values:
60 100 120
Enter weights:
10 20 30
Enter maximum weight capacity: 50
Output:
Execution Time (nanoseconds): 43400
Approximate Memory Used (bytes): 816
Maximum Profit: 220
Wrong Input:
Enter number of items: 4
Enter values:
20 40 50 100
Enter weights:
5 10 15 30
Enter maximum weight capacity: 40
Output:
Execution Time (nanoseconds): 56000
Approximate Memory Used (bytes): 1240
Maximum Profit: 160
Time Complexity Analysis
● The dynamic programming solution to the 0/1 Knapsack problem
has a time complexity of: T(n,W)=O(nW)
● For n = 50 items and a weight limit W = 1000, the number of
operations is:
50×1000 = 50,000 ns = 50 µs
● For n = 100, W = 2000:
100×2000 = 200,000 ns = 200 µs
Space Complexity Analysis
● The algorithm requires a dp[n][W] table, leading to: O(nW)
● For n = 50, W = 1000, assuming each entry takes 4 bytes:
50×1000×4=200,000 bytes=200 KB
● For n = 100, W = 2000:
100×2000×4=800,000 bytes=800 KB