Prim's Algorithm - Minimum Spanning Tree
Prim's Algorithm is a greedy algorithm used to find the Minimum Spanning Tree (MST) of a weighted, connected, and undirected graph. It builds the MST one vertex at a time, starting from an arbitrary root vertex.
At each step, it adds the cheapest edge that connects a vertex already in the MST to a vertex not yet in the MST.
Think of it like building a snowball: you start with a single snowflake (the root), and you roll it downhill, picking up the closest snow (cheapest edge) to grow it into a big snowball. You never pick up snow that is far away if there is closer snow available.
Key Properties
- Greedy algorithm (makes the locally optimal choice at each step).
- Works only for connected, undirected, and weighted graphs.
- The MST is unique if all edge weights are distinct.
How Prim's Algorithm Works (Step-by-Step)
Step-by-Step Process
- Initialize:
- Pick a starting vertex (any vertex). Mark it as visited.
- Maintain a set of edges that connect visited vertices to unvisited vertices (this is called the "cut").
- Find Minimum: Select the edge with the minimum weight from the cut.
- Add to MST: Add this edge and the new vertex to the MST.
- Update Cut: Add all edges from the newly added vertex to the cut.
- Repeat steps 2-4 until all vertices are visited (MST has
V-1edges).
Detailed Example
Consider the following weighted undirected graph:
(2)
A -------- B
| \ / |
| (4) (3) |
| \ / |
(1) C (5)
| / \ |
| (6) (7) |
| / \ |
D -------- E
(8)Vertices: A, B, C, D, E. We start from vertex A.
Step-by-Step Execution
| Step | Visited Set | Cut Edges (Visited → Unvisited) | Chosen Edge | Added Vertex | MST Edges |
|---|---|---|---|---|---|
| 0 | {A} | A-B(2), A-C(4), A-D(1) | A-D(1) | D | {A-D} |
| 1 | {A, D} | A-B(2), A-C(4), D-C(6), D-E(8) | A-B(2) | B | {A-D, A-B} |
| 2 | {A, D, B} | A-C(4), D-C(6), D-E(8), B-C(3), B-E(5) | B-C(3) | C | {A-D, A-B, B-C} |
| 3 | {A, D, B, C} | D-E(8), B-E(5), C-E(7) | B-E(5) | E | {A-D, A-B, B-C, B-E} |
Final MST:
A --- B
| |
D C
|
ETotal Weight = 1 + 2 + 3 + 5 = 11
Algorithm for Prim's Algorithm
Pseudocode
Prim(Graph G, start):
Input: G - Weighted, connected, undirected graph
start - Starting vertex
Output: MST represented as parent array
Step 1: Initialize visited[V] = false
Step 2: Initialize key[V] = ∞
Step 3: key[start] = 0
Step 4: Initialize parent[V] = -1
Step 5: FOR i = 0 to V-1 DO
Step 6: u = vertex with minimum key among unvisited vertices
Step 7: visited[u] = true
Step 8: FOR each neighbor v of u DO
Step 9: IF visited[v] == false AND weight(u,v) < key[v] THEN
Step 10: key[v] = weight(u,v)
Step 11: parent[v] = u
Step 12: END IF
Step 13: END FOR
Step 14: END FOR
Step 15: RETURN parentImplementation in C (Adjacency Matrix)
#include <stdio.h>
#include <stdbool.h>
#include <limits.h>
#define V 5 // Number of vertices
// Function to find the vertex with minimum key value
int minKey(int key[], bool mstSet[]) {
int min = INT_MAX, min_index;
for (int v = 0; v < V; v++) {
if (mstSet[v] == false && key[v] < min) {
min = key[v];
min_index = v;
}
}
return min_index;
}
// Function to print the MST
void printMST(int parent[], int graph[V][V]) {
printf("Edge \t\tWeight\n");
int totalWeight = 0;
for (int i = 1; i < V; i++) {
printf("%d - %d \t\t%d\n", parent[i], i, graph[i][parent[i]]);
totalWeight += graph[i][parent[i]];
}
printf("Total MST Weight: %d\n", totalWeight);
}
// Prim's Algorithm function
void primMST(int graph[V][V]) {
int parent[V]; // Array to store constructed MST
int key[V]; // Key values used to pick minimum weight edge
bool mstSet[V]; // To represent set of vertices included in MST
// Initialize all keys as INFINITE and mstSet as false
for (int i = 0; i < V; i++) {
key[i] = INT_MAX;
mstSet[i] = false;
}
// Start with vertex 0
key[0] = 0;
parent[0] = -1; // First node is always root of MST
// The MST will have V-1 edges
for (int count = 0; count < V - 1; count++) {
// Pick the minimum key vertex from the set of vertices not yet processed
int u = minKey(key, mstSet);
mstSet[u] = true;
// Update key and parent for adjacent vertices
for (int v = 0; v < V; v++) {
// graph[u][v] is non-zero only for adjacent vertices
// mstSet[v] is false for vertices not yet included
// Update the key only if graph[u][v] is smaller than key[v]
if (graph[u][v] && mstSet[v] == false && graph[u][v] < key[v]) {
parent[v] = u;
key[v] = graph[u][v];
}
}
}
printMST(parent, graph);
}
int main() {
// Adjacency matrix representation of the graph
int graph[V][V] = {
{0, 2, 4, 1, 0},
{2, 0, 3, 0, 5},
{4, 3, 0, 6, 7},
{1, 0, 6, 0, 8},
{0, 5, 7, 8, 0}
};
primMST(graph);
return 0;
}Output:
Edge Weight
0 - 3 1
0 - 1 2
1 - 2 3
1 - 4 5
Total MST Weight: 11Implementation in C (Adjacency List with Heap Optimization)
For sparse graphs, using an adjacency list with a binary heap (priority queue) gives better performance.
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <limits.h>
// Adjacency List Node
typedef struct Node {
int vertex;
int weight;
struct Node* next;
} Node;
// Graph structure
typedef struct Graph {
int vertices;
Node** adjLists;
} Graph;
// Min Heap Node
typedef struct HeapNode {
int vertex;
int key;
} HeapNode;
// Min Heap
typedef struct MinHeap {
int size;
int capacity;
int* position; // To track positions for decrease-key
HeapNode** array;
} MinHeap;
// Create a graph
Graph* createGraph(int vertices) {
Graph* graph = (Graph*)malloc(sizeof(Graph));
graph->vertices = vertices;
graph->adjLists = (Node**)malloc(vertices * sizeof(Node*));
for (int i = 0; i < vertices; i++) {
graph->adjLists[i] = NULL;
}
return graph;
}
// Add edge (undirected)
void addEdge(Graph* graph, int src, int dest, int weight) {
Node* newNode = (Node*)malloc(sizeof(Node));
newNode->vertex = dest;
newNode->weight = weight;
newNode->next = graph->adjLists[src];
graph->adjLists[src] = newNode;
newNode = (Node*)malloc(sizeof(Node));
newNode->vertex = src;
newNode->weight = weight;
newNode->next = graph->adjLists[dest];
graph->adjLists[dest] = newNode;
}
// Create a min heap node
HeapNode* createHeapNode(int vertex, int key) {
HeapNode* heapNode = (HeapNode*)malloc(sizeof(HeapNode));
heapNode->vertex = vertex;
heapNode->key = key;
return heapNode;
}
// Create a min heap
MinHeap* createMinHeap(int capacity) {
MinHeap* minHeap = (MinHeap*)malloc(sizeof(MinHeap));
minHeap->position = (int*)malloc(capacity * sizeof(int));
minHeap->size = 0;
minHeap->capacity = capacity;
minHeap->array = (HeapNode**)malloc(capacity * sizeof(HeapNode*));
return minHeap;
}
// Swap two heap nodes
void swapHeapNode(HeapNode** a, HeapNode** b) {
HeapNode* temp = *a;
*a = *b;
*b = temp;
}
// Heapify function for min heap
void heapify(MinHeap* minHeap, int idx) {
int smallest = idx;
int left = 2 * idx + 1;
int right = 2 * idx + 2;
if (left < minHeap->size &&
minHeap->array[left]->key < minHeap->array[smallest]->key) {
smallest = left;
}
if (right < minHeap->size &&
minHeap->array[right]->key < minHeap->array[smallest]->key) {
smallest = right;
}
if (smallest != idx) {
// Update positions
HeapNode* smallestNode = minHeap->array[smallest];
HeapNode* idxNode = minHeap->array[idx];
minHeap->position[smallestNode->vertex] = idx;
minHeap->position[idxNode->vertex] = smallest;
swapHeapNode(&minHeap->array[smallest], &minHeap->array[idx]);
heapify(minHeap, smallest);
}
}
// Check if heap is empty
bool isEmpty(MinHeap* minHeap) {
return minHeap->size == 0;
}
// Extract minimum node from heap
HeapNode* extractMin(MinHeap* minHeap) {
if (isEmpty(minHeap)) {
return NULL;
}
HeapNode* root = minHeap->array[0];
HeapNode* lastNode = minHeap->array[minHeap->size - 1];
minHeap->array[0] = lastNode;
minHeap->position[root->vertex] = minHeap->size - 1;
minHeap->position[lastNode->vertex] = 0;
minHeap->size--;
heapify(minHeap, 0);
return root;
}
// Decrease key value in heap
void decreaseKey(MinHeap* minHeap, int vertex, int key) {
int i = minHeap->position[vertex];
minHeap->array[i]->key = key;
while (i && minHeap->array[i]->key < minHeap->array[(i - 1) / 2]->key) {
minHeap->position[minHeap->array[i]->vertex] = (i - 1) / 2;
minHeap->position[minHeap->array[(i - 1) / 2]->vertex] = i;
swapHeapNode(&minHeap->array[i], &minHeap->array[(i - 1) / 2]);
i = (i - 1) / 2;
}
}
// Check if vertex is in min heap
bool isInMinHeap(MinHeap* minHeap, int vertex) {
return minHeap->position[vertex] < minHeap->size;
}
// Prim's Algorithm using adjacency list and min heap
void primMST(Graph* graph) {
int V = graph->vertices;
int parent[V];
int key[V];
// Initialize keys and parents
for (int i = 0; i < V; i++) {
key[i] = INT_MAX;
parent[i] = -1;
}
// Create min heap
MinHeap* minHeap = createMinHeap(V);
// Initialize heap with all vertices
for (int i = 0; i < V; i++) {
minHeap->array[i] = createHeapNode(i, key[i]);
minHeap->position[i] = i;
}
// Start with vertex 0
key[0] = 0;
minHeap->array[0]->key = 0;
minHeap->size = V;
// While heap is not empty
while (!isEmpty(minHeap)) {
HeapNode* minNode = extractMin(minHeap);
int u = minNode->vertex;
// Traverse all neighbors of u
Node* temp = graph->adjLists[u];
while (temp != NULL) {
int v = temp->vertex;
// If v is in heap and weight is less than current key
if (isInMinHeap(minHeap, v) && temp->weight < key[v]) {
key[v] = temp->weight;
parent[v] = u;
decreaseKey(minHeap, v, key[v]);
}
temp = temp->next;
}
}
// Print MST
printf("MST Edges (Prim's Algorithm with Heap):\n");
printf("Edge \tWeight\n");
int totalWeight = 0;
for (int i = 1; i < V; i++) {
printf("%d - %d \t%d\n", parent[i], i, key[i]);
totalWeight += key[i];
}
printf("Total Weight: %d\n", totalWeight);
}
int main() {
Graph* graph = createGraph(5);
addEdge(graph, 0, 1, 2);
addEdge(graph, 0, 2, 4);
addEdge(graph, 0, 3, 1);
addEdge(graph, 1, 2, 3);
addEdge(graph, 1, 4, 5);
addEdge(graph, 2, 3, 6);
addEdge(graph, 2, 4, 7);
addEdge(graph, 3, 4, 8);
primMST(graph);
return 0;
}Output:
MST Edges (Prim's Algorithm with Heap):
Edge Weight
0 - 3 1
0 - 1 2
1 - 2 3
1 - 4 5
Total Weight: 11Complexity Analysis
Time Complexity
| Implementation | Time Complexity |
|---|---|
| Adjacency Matrix | O(V²) |
| Adjacency List + Binary Heap | O(E log V) |
| Adjacency List + Fibonacci Heap | O(E + V log V) |
Space Complexity
- O(V) for the auxiliary arrays (key, parent, visited).
- O(V + E) for the adjacency list representation.
Advantages of Prim's Algorithm
- Efficient for Dense Graphs: The adjacency matrix implementation (O(V²)) is simple and works well when the graph is dense (many edges).
- Guaranteed Optimal: Being a greedy algorithm, it always produces the minimum spanning tree.
- Simple Concept: The idea of growing the tree from a single vertex is intuitive.
Disadvantages of Prim's Algorithm
- Requires Connected Graph: Prim's algorithm only works for connected graphs.
- Memory Overhead: The heap implementation requires extra memory for the heap and position tracking.
- Not Suitable for Disconnected Graphs: It cannot find a spanning tree for disconnected graphs.
Prim's vs. Kruskal's: Quick Comparison
| Feature | Prim's Algorithm | Kruskal's Algorithm |
|---|---|---|
| Approach | Vertex-based (grows a tree) | Edge-based (adds cheapest edges) |
| Data Structure | Heap / Priority Queue (or adjacency matrix) | Union-Find (Disjoint Set) |
| Best For | Dense graphs | Sparse graphs |
| Time Complexity | O(V²) (matrix) / O(E log V) (heap) | O(E log E) |
| Cycle Detection | Avoids cycles by tracking visited vertices | Uses Union-Find to detect cycles |
Practice Questions
1. Given the following graph, find the MST using Prim's algorithm starting from vertex 0:
0
/|\
1 2 3
/|\
4 5 6
Was this article helpful?