
PDSA - Week 5Weighted GraphWeighted directed graph Adjacency matrix representation in PythonAdjacency list representation in PythonWeighted undirected graph Adjacency matrix representation in PythonAdjacency list representation in PythonShortest PathSingle source shortest path algorithmDijkstra's AlgorithmAlgorithm StepsWorking Visualization Implementation Dijkstra's For Adjacency matrixImplementation Dijkstra's For Adjacency ListBellman Ford algorithmAlgorithm StepsWorking VisualizationImplementation of Bellman Ford for adjacency matrixImplementation of Bellman Ford for adjacency listAll pair of shortest pathFloyd-Warshall algorithmAlgorithm StepsImplementation of Floyd Warshall AlgorithmSpanning Tree(ST)Minimum Cost Spanning Tree(MCST)Prim's AlgorithmAlgorithm StepsWorking VisualizationImplementation of Prim's AlgorithmKruskal's AlgorithmAlgorithm StepsWorking VisualizationImplementation of Kruskal's Algorithm
1# Each tuple (u,v,d) in list is representing the edge from u to v with weight d2dedges = [(0,1,10),(0,2,80),(1,2,6),(1,4,20),(2,3,70),(4,5,50),(4,6,5),(5,6,10)]3size = 74import numpy as np5W = np.zeros(shape=(size,size,2))6for (i,j,w) in dedges:7 W[i,j,0] = 18 W[i,j,1] = w9print(W)
xxxxxxxxxx81dedges = [(0,1,10),(0,2,80),(1,2,6),(1,4,20),(2,3,70),(4,5,50),(4,6,5),(5,6,10)]2size = 73WL = {}4for i in range(size):5 WL[i] = []6for (i,j,d) in dedges:7 WL[i].append((j,d))8print(WL)
xxxxxxxxxx101dedges = [(0,1,10),(0,2,80),(1,2,6),(1,4,20),(2,3,70),(4,5,50),(4,6,5),(5,6,10)]2# Each edge represented by two entry in data structure ((u,v,d) and (v,u,d) for undirected graph3edges = dedges + [(j,i,w) for (i,j,w) in dedges]4size = 75import numpy as np6W = np.zeros(shape=(size,size,2))7for (i,j,w) in edges:8 W[i,j,0] = 19 W[i,j,1] = w10print(W)
xxxxxxxxxx91dedges = [(0,1,10),(0,2,80),(1,2,6),(1,4,20),(2,3,70),(4,5,50),(4,6,5),(5,6,10)]2edges = dedges + [(j,i,w) for (i,j,w) in dedges]3size = 74WL = {}5for i in range(size):6 WL[i] = []7for (i,j,d) in edges:8 WL[i].append((j,d))9print(WL)
Find shortest paths from a source vertex to every other vertex.
Dijkstra's Algorithm
Bellman Ford algorithm
Dijkstra's algorithm is a popular algorithm for finding the shortest path between two vertices in a weighted graph. Some of its important properties and points are:
Single-source shortest path: Dijkstra's algorithm finds the shortest path from a single source vertex to all other vertices in the graph.
Weighted graph: Dijkstra's algorithm only works on weighted graphs, where each edge has a weight or cost associated with it.
Non-negative weights: Dijkstra's algorithm can only be used on graphs with non-negative edge weights. If the graph contains negative weights, Bellman-Ford algorithm is a better choice.
Greedy algorithm: Dijkstra's algorithm is a greedy algorithm that selects the vertex with the smallest distance from the starting vertex and explores its neighbors. This process is repeated until the shortest path to all vertices is found.
Optimal substructure: Dijkstra's algorithm relies on the optimal substructure property, which means that the optimal solution to a problem can be obtained by combining the optimal solutions to its subproblems.
Can handle disconnected graphs: Dijkstra's algorithm can handle disconnected graphs, but it will only find the shortest path for the connected component containing the source vertex.
Here are the steps for Dijkstra's algorithm for finding the single source shortest path:
Create a table to store the distances from the source vertex to each vertex in the graph. Initialize the source vertex with a distance of 0 and all other vertices with infinity. Also create a set of unvisited vertices and mark all vertices as unvisited.
While the set of unvisited vertices is not empty, do the following:
Select the unvisited vertex with the smallest distance from the source vertex. This vertex is now considered visited.
For each neighbor of the visited vertex that is still unvisited, calculate the distance to that neighbor by adding the weight of the edge between the visited vertex and the neighbor to the distance of the visited vertex. If this distance is smaller than the distance currently stored for the neighbor in the table, update the table with the new distance.
After updating the distances for all neighbors, mark the visited vertex as visited.
Repeat steps 3 to 5 until all vertices have been visited or the destination vertex has been visited.
Once the algorithm has visited all vertices, the table will contain the shortest distances from the source vertex to each vertex in the graph.
To find the shortest path from the source vertex to a destination vertex, backtrack from the destination vertex to the source vertex by following the path with the smallest distance at each step. This will give you the shortest path from the source vertex to the destination vertex.
Select Dijkstra(s) form left bottom menu
For given graph

x1def dijkstra(WMat,s):2 #Initialization3 (rows,cols,x) = WMat.shape4 infinity = np.max(WMat)*rows+15 (visited,distance) = ({},{})6 for v in range(rows):7 (visited[v],distance[v]) = (False,infinity) 8 distance[s] = 09 10 # Computing shortest distance for each vertex from source11 for u in range(rows):12 # Find minimum distance value on vertices which are not visited13 min_dist = min([distance[v] for v in range(rows) if not visited[v]])14 15 # Find vertices which have minimum distance value min_dist16 nextv_list = [v for v in range(rows)if (not visited[v]) and distance[v] == min_dist]17 18 # Select minimum level vertex which have minimum distance value min_dist and mark visited19 nextv = min(nextv_list)20 visited[nextv] = True21 22 # Check for each adjacent of nextv vertex which is not visited23 for v in range(cols): 24 if WMat[nextv,v,0] == 1 and (not visited[v]):25 #If distance of v through nextv is smaller than the current distance on v, then update26 if distance[nextv] + WMat[nextv,v,1] < distance[v]:27 distance[v] = distance[nextv] + WMat[nextv,v,1]28 return(distance)29
30
31
32dedges = [(0,1,10),(0,2,80),(1,2,6),(1,4,20),(2,3,70),(4,5,50),(4,6,5),(5,6,10)]33size = 734import numpy as np35W = np.zeros(shape=(size,size,2))36for (i,j,w) in dedges:37 W[i,j,0] = 138 W[i,j,1] = w39print(dijkstra(W,0))Output
xxxxxxxxxx11{0: 0, 1: 10.0, 2: 16.0, 3: 86.0, 4: 30.0, 5: 80.0, 6: 35.0}Complexity
xxxxxxxxxx381def dijkstralist(WList,s):2 #Initialization3 infinity = 1 + len(WList.keys())*max([d for u in WList.keys() for (v,d) in WList[u]])4 (visited,distance) = ({},{})5 for v in WList.keys():6 (visited[v],distance[v]) = (False,infinity) 7 distance[s] = 08 9 # Computing shortest distance for each vertex from source10 for u in WList.keys():11 # Find minimum distance value on vertices which are not visited12 min_dist = min([distance[v] for v in WList.keys() if not visited[v]])13 14 # Find vertices which have minimum distance value min_dist15 nextv_list = [v for v in WList.keys() if (not visited[v]) and distance[v] == min_dist]16
17 # Select minimum level vertex which have minimum distance value min_dist and mark visited18 nextv = min(nextv_list)19 visited[nextv] = True20 21 # Check for each adjacent of nextv vertex which is not visited22 for (v,d) in WList[nextv]:23 if not visited[v]:24 # If distance of v through nextv is smaller than the current distance of v, then update25 if distance[nextv]+d < distance[v]:26 distance[v] = distance[nextv]+d27 return(distance)28
29
30
31dedges = [(0,1,10),(0,2,80),(1,2,6),(1,4,20),(2,3,70),(4,5,50),(4,6,5),(5,6,10)]32size = 733WL = {}34for i in range(size):35 WL[i] = []36for (i,j,d) in dedges:37 WL[i].append((j,d))38print(dijkstralist(WL,0))Output
xxxxxxxxxx11{0: 0, 1: 10, 2: 16, 3: 86, 4: 30, 5: 80, 6: 35}Code Execution Flow
Complexity
Bellman-Ford algorithm is a dynamic programming based algorithm to find the shortest path in a weighted graph, where the edge weights may be negative. Here are some important points and properties of the algorithm:
It can handle graphs with negative edge weights: Unlike Dijkstra's algorithm, which can only handle non-negative edge weights, the Bellman-Ford algorithm can handle graphs with negative edge weights. However, it cannot handle graphs with negative cycles, which are cycles that have a negative sum of edge weights.
It can detect negative cycles: The Bellman-Ford algorithm can detect negative cycles in a graph. If there is a negative cycle, the algorithm will report that there is no shortest path from the source vertex to some other vertex.
It can handle disconnected graphs: The Bellman-Ford algorithm can handle disconnected graphs by finding the shortest paths from the source vertex to all other vertices in each connected component.
It uses dynamic programming: The Bellman-Ford algorithm uses dynamic programming to solve the shortest path problem. It maintains an array of distances that is gradually updated until it converges to the correct values.
Bellman-Ford works for both directed and undirected graphs with non-negative edges weights.
Here are the step-by-step instructions for the Bellman-Ford algorithm:
Initialize the distance array: Set the distance of the source vertex to 0 and the distances of all other vertices to infinity.
Relax all edges: Repeat the following step (V-1) times, where V is the number of vertices in the graph. For each edge (u,v) with weight w, if the distance to u plus w is less than the distance to v, update the distance to v to the distance to u plus w.
Check for negative weight cycles: After relaxing all edges V-1 times, check for negative weight cycles. For each edge (u,v) with weight w, if the distance to u plus w is less than the distance to v, there exists a negative weight cycle.
Print the distance array: If there are no negative weight cycles, print the distance array, which contains the shortest path distances from the source vertex to all other vertices.
Select BellmanFord(s) form left bottom menu
For given graph

xxxxxxxxxx301def bellmanford(WMat,s):2 # Initialization3 (rows,cols,x) = WMat.shape4 infinity = np.max(WMat)*rows+15 distance = {}6 for v in range(rows):7 distance[v] = infinity 8 distance[s] = 09 10 # Computing shortest distance for each vertex from source11 # Repeat the process n times where n is number of vertices12 for i in range(rows):13 # Check for each adjacent of u vertex14 for u in range(rows):15 for v in range(cols):16 if WMat[u,v,0] == 1:17 # If distance of v through u is smaller than the current distance of v, then update18 if distance[u] + WMat[u,v,1] < distance[v]:19 distance[v] = distance[u] + WMat[u,v,1]20 return(distance)21
22
23edges = [(0,1,10),(0,7,8),(1,5,2),(2,1,1),(2,3,1),(3,4,3),(4,5,-1),(5,2,-2),(6,1,-4),(6,5,-1),(7,6,1)]24size = 825import numpy as np26W = np.zeros(shape=(size,size,2))27for (i,j,w) in edges:28 W[i,j,0] = 129 W[i,j,1] = w 30print(bellmanford(W,0))Output
xxxxxxxxxx11{0: 0, 1: 5.0, 2: 5.0, 3: 6.0, 4: 9.0, 5: 7.0, 6: 9.0, 7: 8.0}Complexity
xxxxxxxxxx281def bellmanfordlist(WList,s):2 # Initialization3 infinity = 1 + len(WList.keys())*max([d for u in WList.keys() for (v,d) in WList[u]])4 distance = {}5 for v in WList.keys():6 distance[v] = infinity 7 distance[s] = 08 9 # Computing shortest distance for each vertex from source10 # Repeat the process n times where n is number of vertices 11 for i in WList.keys():12 # Check for each adjacent of u vertex13 for u in WList.keys():14 for (v,d) in WList[u]:15 # If distance of v through u is smaller than the current distance of v, then update16 if distance[u] + d < distance[v]:17 distance[v] = distance[u] + d18 return(distance)19
20
21edges = [(0,1,10),(0,7,8),(1,5,2),(2,1,1),(2,3,1),(3,4,3),(4,5,-1),(5,2,-2),(6,1,-4),(6,5,-1),(7,6,1)]22size = 823WL = {}24for i in range(size):25 WL[i] = []26for (i,j,d) in edges:27 WL[i].append((j,d))28print(bellmanfordlist(WL,0))Output
xxxxxxxxxx11{0: 0, 1: 5, 2: 5, 3: 6, 4: 9, 5: 7, 6: 9, 7: 8}
Code Execution Flow
Complexity
Find the shortest paths between every pair of vertices i and j.
It is equivalent to if run Dijkstra or Bellman-Ford from each vertex.
The Floyd-Warshall algorithm is an efficient algorithm for finding the shortest path between all pairs of nodes in a weighted graph. Some important points and properties of the Floyd-Warshall algorithm are:
All-pairs shortest path: The Floyd-Warshall algorithm computes the shortest path between all pairs of nodes in the graph.
Negative cycles: The Floyd-Warshall algorithm can detect negative cycles in the graph. A negative cycle is a cycle in the graph where the sum of the weights of the edges is negative. If there is a negative cycle in the graph, then the algorithm will report that there is a negative cycle.
Dynamic programming: The Floyd-Warshall algorithm uses dynamic programming to compute the shortest path between all pairs of nodes in the graph. The algorithm builds a table of shortest paths between pairs of nodes by iteratively considering intermediate nodes along the path.
No guarantee of uniqueness: The shortest path between two nodes in a graph may not be unique. If there are multiple shortest paths between two nodes, then the Floyd-Warshall algorithm may return any one of them.
Floyd-Warshall's works for both directed and undirected graphs with non-negative edges weights.
Floyd-Warshall's does not work with an undirected graph with negative edges weight, as it will be declared as a negative weight cycle.
Here are the steps for the algorithm:
Initialization: Create a 2-dimensional array SP of size n x n, where n is the number of vertices in the graph. For each pair of vertices (i,j), initialize SP[i][j] to the weight of the edge from vertex i to vertex j. If there is no edge between vertices i and j, then set SP[i][j] to infinity.
For each vertex k from 1 to n, compute the shortest path between every pair of vertices (i,j) that passes through k. To do this, update SP[i][j] as follows:
SP[i][j] = min(SP[i][j], SP[i][k] + SP[k][j])
This means that the shortest path from vertex i to vertex j that passes through k is the minimum of the current shortest path from i to j and the sum of the shortest path from i to k and the shortest path from k to j.
After the step 2 is complete, the SP array will contain the shortest path between every pair of vertices in the graph.
For given input graph

xxxxxxxxxx331def floydwarshall(WMat):2 # Initialization3 (rows,cols,x) = WMat.shape4 infinity = float('inf') 5 SP = np.zeros(shape=(rows,cols,cols+1))6 7 # Filling the initial graph entry in matrix8 for i in range(rows):9 for j in range(cols):10 if WMat[i,j,0] == 1:11 SP[i,j,0] = WMat[i,j,1]12 else:13 SP[i,j,0] = infinity14 15 # Repeat the process n times where n is number of vertices16 for k in range(1,cols+1):17 # Checking The shortest path distance for each pair in matrix 18 for i in range(rows):19 for j in range(cols):20 SP[i,j,k] = min(SP[i,j,k-1],SP[i,k-1,k-1]+SP[k-1,j,k-1])21 22 # Retuen the last updated matrix23 return(SP[:,:,cols])24
25
26edges = [(0,1,10),(0,7,8),(1,5,2),(2,1,1),(2,3,1),(3,4,3),(4,5,-1),(5,2,-2),(6,1,-4),(6,5,-1),(7,6,1)]27size = 828import numpy as np29W = np.zeros(shape=(size,size,2))30for (i,j,w) in edges:31 W[i,j,0] = 132 W[i,j,1] = w 33print(floydwarshall(W))Output
xxxxxxxxxx81[[inf 5. 5. 6. 9. 7. 9. 8.]2[inf 1. 0. 1. 4. 2. inf inf]3[inf 1. 1. 1. 4. 3. inf inf]4[inf 1. 0. 1. 3. 2. inf inf]5[inf -2. -3. -2. 1. -1. inf inf]6[inf -1. -2. -1. 2. 1. inf inf]7[inf -4. -4. -3. 0. -2. inf inf]8[inf -3. -3. -2. 1. -1. 1. inf]]
Complexity
Retain a minimal set of edges so that graph remains connected
Recall that a minimally connected graph is a tree
Adding an edge to a tree creates a loop
Removing an edge disconnects the graph
Want a tree that connects all the vertices — spanning tree
More than one spanning tree, in general
Add the cost of all the edges in the tree
Among the different spanning trees, choose one with minimum cost
Some facts about trees
A tree on n vertices has exactly n − 1 edges
Adding an edge to a tree must create a cycle.
In a tree, every pair of vertices is connected by a unique path
Algorithms:-
Prim's Algorithm
Kruskal's Algorithm
Prim's algorithm is a greedy algorithm that finds a minimum spanning tree (MST) for a weighted undirected graph. Here are some of the key points or properties of Prim's algorithm:
Greedy approach: Prim's algorithm is a greedy algorithm that works by building up the MST one vertex at a time, always choosing the cheapest edge that connects the growing tree to an outside vertex.
Works on connected graphs: Prim's algorithm only works on connected graphs, meaning that there must be a path between any two vertices in the graph.
Weighted edges: Prim's algorithm requires that the graph have weighted edges. The weights represent the cost or distance of traveling between two vertices.
Spanning tree: Prim's algorithm always produces a spanning tree, which is a subset of the edges that connects all vertices in the graph and contains no cycles.
Unique solution: If the weights of the edges are unique, then the MST produced by Prim's algorithm is unique.
Here are the steps for the algorithm:
Choose any vertex as the starting vertex and add it to the MST set. Initialize the weight of this vertex to 0.
Find the edge with the minimum weight that connects the starting vertex to any other vertex.
Add the adjacent vertex connected by the minimum weight edge to the MST set. Set its weight to the weight of the edge.
Repeat steps 2 and 3 until all vertices are included in the MST set.
Select Prim's Algorithm from bottom-left menu
Source:- https://visualgo.net/en/mst
For given input graph

xxxxxxxxxx451def primlist(WList):2 # Initialization3 infinity = 1 + max([d for u in WList.keys() for (v,d) in WList[u]])4 (visited,distance,TreeEdges) = ({},{},[])5 for v in WList.keys():6 (visited[v],distance[v]) = (False,infinity)7 8 # Start from vertex 0, marked visited and update the distance of adjacents from 09 visited[0] = True10 for (v,d) in WList[0]:11 distance[v] = d12 13 # Repeat the below process (number of vertices-1) times14 for i in range(1,len(WList.keys())):15 mindist = infinity16 nextv = None17 #Finding the minimum weight edge (u,v) where vertex u is visited and v is not visited 18 for u in WList.keys():19 for (v,d) in WList[u]:20 if visited[u] and (not visited[v]) and d < mindist:21 mindist = d22 nextv = v23 nexte = (u,v)24 25 # Mark nextv as visited and append the nexte in MST26 visited[nextv] = True27 TreeEdges.append(nexte)28 29 # Update the distance of unvisited adjacents of nextv if smaller than current30 for (v,d) in WList[nextv]:31 if not visited[v]:32 if d < distance[v]:33 distance[v] = d34 return(TreeEdges)35
36
37dedges = [(0,1,10),(0,3,18),(1,2,20),(1,3,6),(2,4,8),(3,4,70)]38edges = dedges + [(j,i,w) for (i,j,w) in dedges]39size = 540WL = {}41for i in range(size):42 WL[i] = []43for (i,j,d) in edges:44 WL[i].append((j,d))45print(primlist(WL))Output
xxxxxxxxxx11[(0, 1), (1, 3), (1, 2), (2, 4)]
Output minimum spanning tree with cost 44

Code Execution Flow
Other Implementation
xxxxxxxxxx411def primlist2(WList):2 # Initialization3 infinity = float("inf")4 (visited,distance,nbr) = ({},{},{})5 for v in WList.keys():6 (visited[v],distance[v],nbr[v]) = (False,infinity,-1)7 8 # Set start vertex distance to 0 9 distance[0] = 010 11 # Repeat the below process (number of vertices-1) times12 for i in range(0,len(WList.keys())):13 # Find minimum distance value on vertices which are not visited14 min_dist = min([distance[v] for v in WList.keys() if not visited[v]])15 16 # Find all vertices that have minimum distance value min_dist and not visited17 nextv_list = [v for v in WList.keys() if (not visited[v]) and distance[v] == min_dist]18 19 # Select the minimum level value vertex from nextv_list ans mark visited20 nextv = min(nextv_list) 21 visited[nextv] = True22 23 # Update the nbr or parent value for v with minimum eadge distance24 for (v,d) in WList[nextv]:25 if not visited[v]:26 if d < distance[v]:27 nbr[v] = nextv28 distance[v] = d29 return(nbr)30
31
32
33dedges = [(0,1,10),(0,3,18),(1,2,20),(1,3,6),(2,4,8),(3,4,70)]34edges = dedges + [(j,i,w) for (i,j,w) in dedges]35size = 536WL = {}37for i in range(size):38 WL[i] = []39for (i,j,d) in edges:40 WL[i].append((j,d))41print(primlist2(WL))Output
xxxxxxxxxx11{0: -1, 1: 0, 2: 1, 3: 1, 4: 2}
Code Execution Flow
Complexity
Kruskal's algorithm is a greedy algorithm used to find the minimum spanning tree (MST) of a connected, undirected graph. The algorithm works by iteratively adding the edges of the graph with the smallest weights that do not create a cycle in the MST. Here are some important points and properties of Kruskal's algorithm:
Kruskal's algorithm is a greedy algorithm used to find the minimum spanning tree (MST) of a connected, undirected graph.
The algorithm works by iteratively adding the edges of the graph with the smallest weights that do not create a cycle in the MST.
Kruskal's algorithm guarantees that the MST is unique if the graph has unique edge weights.
Here are the steps for the of Kruskal's algorithm:
Sort all the edges of the graph in non-decreasing order of their weights.
Create a new empty set for the minimum spanning tree (MST).
Iterate through the sorted edges, starting with the edge with the smallest weight.
For each edge, check if adding it to the MST will create a cycle.
If the edge does not create a cycle, add it to the MST.
Repeat steps 4 and 5 until all vertices are included in the MST, or until the desired number of edges have been added.
Return the MST.
Select Kruskal's Algorithm from bottom-left menu
Source:- https://visualgo.net/en/mst
For given input graph

xxxxxxxxxx331def kruskal(WList):2 # Initialization3 (edges,component,TE) = ([],{},[])4 for u in WList.keys():5 # Weight as first value in tuple to sort easily6 edges.extend([(d,u,v) for (v,d) in WList[u]])7 # Initially each vertex as single components and assign leader of each component 8 component[u] = u9 10 # Sort the edges in increasing order of their weights11 edges.sort()12 13 for (d,u,v) in edges:14 # If (u,v) edge is not creating the cycle in MST, add the edge in MST15 if component[u] != component[v]:16 TE.append((u,v))17 c = component[u]18 # Update of component leader 19 for w in WList.keys():20 if component[w] == c:21 component[w] = component[v]22 return(TE)23
24
25dedges = [(0,1,10),(0,2,18),(1,2,6),(1,4,20),(2,3,70),(4,5,10),(4,6,10),(5,6,5)]26edges = dedges + [(j,i,w) for (i,j,w) in dedges]27size = 728WL = {}29for i in range(size):30 WL[i] = []31for (i,j,d) in edges:32 WL[i].append((j,d))33print(kruskal(WL))Output
xxxxxxxxxx11[(5, 6), (1, 2), (0, 1), (4, 5), (1, 4), (2, 3)]
Output minimum spanning tree with cost 121

Code Execution Flow
Complexity