Prim’s algorithm is a greedy algorithm used to find the Minimum Spanning Tree (MST) of a connected, undirected graph. The MST is a subset of the edges that connects all vertices in the graph with the minimum possible total edge weight.
Examples:
Input: Number of vertices: 5, Edges: [(0, 1, 2), (0, 3, 6), (1, 2, 3), (1, 3, 8), (1, 4, 5), (2, 4, 7), (3, 4, 9)] Output: MST Edges: [(0, 1), (1, 2), (0, 3), (1, 4)], Total weight: 16 Explanation: The MST includes edges (0, 1), (1, 2), (0, 3), and (1, 4) with a total weight of 2 + 3 + 6 + 5 = 16.
Input: Number of vertices: 4, Edges: [(0, 1, 10), (0, 2, 6), (0, 3, 5), (1, 3, 15), (2, 3, 4)] Output: MST Edges: [(2, 3), (0, 3), (0, 1)], Total weight: 19 Explanation: The MST includes edges (2, 3), (0, 3), and (0, 1) with a total weight of 4 + 5 + 10 = 19.
Approach:
To implement Prim’s algorithm in Python, follow these steps:
- Initialize:
- Start with an arbitrary vertex (usually vertex 0) and mark it as part of the MST.
- Use a priority queue (min-heap) to keep track of the edges connecting the MST to the remaining vertices, prioritized by their weights.
- Iterate:
- Extract the minimum weight edge from the priority queue.
- If the destination vertex of this edge is not already in the MST, add the edge to the MST and mark the vertex as part of the MST.
- Add all edges from this newly added vertex to the priority queue, provided the destination vertices are not already in the MST.
- Repeat:
- Continue the process until all vertices are included in the MST.
Below is the implementation of the algorithm:
Python
import heapq
# This class represents a directed graph using adjacency list representation
class Graph:
def __init__(self, V):
self.V = V
self.adj = [[] for _ in range(V)]
# Function to add an edge to the graph
def add_edge(self, u, v, w):
self.adj[u].append((v, w))
self.adj[v].append((u, w))
# Function to print MST using Prim's algorithm
def prim_mst(self):
pq = [] # Priority queue to store vertices that are being processed
src = 0 # Taking vertex 0 as the source
# Create a list for keys and initialize all keys as infinite (INF)
key = [float('inf')] * self.V
# To store the parent array which, in turn, stores MST
parent = [-1] * self.V
# To keep track of vertices included in MST
in_mst = [False] * self.V
# Insert source itself into the priority queue and initialize its key as 0
heapq.heappush(pq, (0, src))
key[src] = 0
# Loop until the priority queue becomes empty
while pq:
# The first vertex in the pair is the minimum key vertex
# Extract it from the priority queue
# The vertex label is stored in the second of the pair
u = heapq.heappop(pq)[1]
# Different key values for the same vertex may exist in the priority queue.
# The one with the least key value is always processed first.
# Therefore, ignore the rest.
if in_mst[u]:
continue
in_mst[u] = True # Include the vertex in MST
# Iterate through all adjacent vertices of a vertex
for v, weight in self.adj[u]:
# If v is not in MST and the weight of (u, v) is smaller than the current key of v
if not in_mst[v] and key[v] > weight:
# Update the key of v
key[v] = weight
heapq.heappush(pq, (key[v], v))
parent[v] = u
# Print edges of MST using the parent array
for i in range(1, self.V):
print(f"{parent[i]} - {i}")
# Driver program to test methods of the graph class
if __name__ == "__main__":
# Create the graph given in the above figure
V = 5
g = Graph(V)
(0, 1, 2), (0, 3, 6), (1, 2, 3), (1, 3, 8), (1, 4, 5), (2, 4, 7), (3, 4, 9)
g.add_edge(0, 1, 2)
g.add_edge(0, 3, 6)
g.add_edge(1, 2, 3)
g.add_edge(1, 3, 8)
g.add_edge(1, 4, 5)
g.add_edge(2, 4, 7)
g.add_edge(3, 4, 9)
g.prim_mst()
Output0 - 1
1 - 2
0 - 3
1 - 4
Complexity Analysis:
- Time Complexity:
- Building the adjacency list takes O(E), where E is the number of edges.
- Each edge is processed at most once, and operations on the priority queue (heap) take O(logV) time, where V is the number of vertices.
- The overall time complexity is O((V+E)logV).
- Space Complexity:
- The space complexity is O(V+E) due to the adjacency list and the priority queue.
|