Prim's Algorithm
Prim's Algorithm is a well-known algorithm in graph theory used to find the Minimum Spanning Tree (MST) of a connected, weighted graph. A Minimum Spanning Tree is a subset of the edges that connects all vertices together, without forming any cycles, and with the minimum possible total edge weight.
Unlike Kruskal’s Algorithm, which operates on edges, Prim’s Algorithm operates on vertices and incrementally grows the MST by adding the smallest edge that connects a vertex in the MST to a vertex outside it. It’s a greedy algorithm, meaning it makes the locally optimal choice at each step with the hope of finding the global optimum.
A spanning tree of a graph is a tree that includes all the vertices of the graph. Since a tree with vertices has exactly edges, the minimum spanning tree (MST) is the spanning tree that has the smallest total edge weight.
Prim’s Algorithm starts with an arbitrary vertex and grows the MST by continuously adding the minimum-weight edge that connects the tree to a vertex outside the tree. Here's how it works:
Initialize: Start with an arbitrary vertex and mark it as part of the MST. Set the key values (which represent the minimum edge weights) of all other vertices to infinity.
Iterate:
Repeat the process until all vertices are included in the MST.
Result: Once all vertices are part of the MST, the algorithm terminates, and we have the Minimum Spanning Tree.
Consider the following graph with 5 nodes:
2 3
A -------- B -------- C
| | |
| 6 | 5 | 7
| | |
D -------- E -------- F
1
Let's look at how to implement Prim’s Algorithm in both Python and C++.
import heapq
def prim(graph, start):
# Initialize a priority queue and the visited set
min_heap = [(0, start)] # (weight, vertex)
mst = []
visited = set()
total_weight = 0
while min_heap:
weight, u = heapq.heappop(min_heap) # Get the vertex with the smallest edge
if u not in visited:
visited.add(u)
mst.append((u, weight))
total_weight += weight
# Add the adjacent vertices to the priority queue
for v, w in graph[u]:
if v not in visited:
heapq.heappush(min_heap, (w, v))
return mst, total_weight
# Sample graph (adjacency list representation)
graph = {
'A': [('B', 2), ('D', 6)],
'B': [('A', 2), ('C', 3), ('E', 5)],
'C': [('B', 3), ('F', 7)],
'D': [('A', 6), ('E', 1)],
'E': [('B', 5), ('D', 1), ('F', 4)],
'F': [('C', 7), ('E', 4)],
}
start_node = 'A'
mst, total_weight = prim(graph, start_node)
print("Edges in the Minimum Spanning Tree:", mst)
print("Total weight of the MST:", total_weight)
Explanation:
min_heap
) is used to always pick the next vertex with the smallest edge weight.
#include <iostream>
#include <vector>
#include <queue>
#include <climits>
#include <unordered_map>
using namespace std;
typedef pair<int, char> pii;
void prim(unordered_map<char, vector<pii>>& graph, char start) {
unordered_map<char, int> key;
unordered_map<char, char> parent;
unordered_map<char, bool> inMST;
for (auto& node : graph) {
key[node.first] = INT_MAX;
inMST[node.first] = false;
}
key[start] = 0;
priority_queue<pii, vector<pii>, greater<pii>> pq;
pq.push({0, start});
while (!pq.empty()) {
char u = pq.top().second;
pq.pop();
inMST[u] = true;
// Update the adjacent vertices
for (auto& neighbor : graph[u]) {
char v = neighbor.first;
int weight = neighbor.second;
if (!inMST[v] && weight < key[v]) {
key[v] = weight;
parent[v] = u;
pq.push({key[v], v});
}
}
}
// Print the MST
cout << "Edges in the Minimum Spanning Tree:" << endl;
int totalWeight = 0;
for (auto& node : parent) {
if (node.second != 0) {
cout << node.second << " - " << node.first << " with weight " << key[node.first] << endl;
totalWeight += key[node.first];
}
}
cout << "Total weight of the MST: " << totalWeight << endl;
}
int main() {
unordered_map<char, vector<pii>> graph;
graph['A'] = {{'B', 2}, {'D', 6}};
graph['B'] = {{'A', 2}, {'C', 3}, {'E', 5}};
graph['C'] = {{'B', 3}, {'F', 7}};
graph['D'] = {{'A', 6}, {'E', 1}};
graph['E'] = {{'B', 5}, {'D', 1}, {'F', 4}};
graph['F'] = {{'C', 7}, {'E', 4}};
char start = 'A';
prim(graph, start);
return 0;
}
Explanation:
key
stores the minimum edge weight for each vertex, and parent
keeps track of the vertex from which it was added to the MST.Time Complexity:
Space Complexity: O(V + E), where V is the number of vertices and E is the number of edges. We need space to store the graph, the key values, the parent pointers, and the priority queue.
Prim's Algorithm is used in several real-world applications, such as: