Prism MST
#include <bits/stdc++.h> // Includes common standard libraries like iostream, unordered_map, list, vector, limits.using namespace std; // Use the standard namespace.
// Function to calculate the Minimum Spanning Tree (MST) using Prim's Algorithm.// This is the basic O(V^2) implementation.// 'edges': A vector of vectors, where each inner vector {u, v, w} represents an undirected edge u-v with weight w.// 'n': Total number of nodes (vertices). Assumed to be 0-indexed (0 to n-1).// 'm': Total number of edges.// Returns a vector of edges forming the MST, where each edge is {{u, v}, weight}.vector<pair<pair<int, int>, int>> calculatePrimsMST(vector<vector<int>> &edges, int n, int m) { // 1. Create Adjacency List for the undirected weighted graph. // 'adjList' stores: Key: node, Value: list of pairs {neighbor_node, weight_to_neighbor}. unordered_map<int, list<pair<int, int>>> adjList; for(int i = 0; i < m; i++) { int u = edges[i][0]; // Source node of an edge. int v = edges[i][1]; // Destination node of an edge. int w = edges[i][2]; // Weight of the edge.
adjList[u].push_back({v, w}); // Add edge u - v with weight w. adjList[v].push_back({u, w}); // Add edge v - u with weight w (for undirected). }
// 2. Initialize required data structures for Prim's Algorithm. // 'key' vector: Stores the minimum weight of an edge connecting node 'i' to the current MST. // Initialized to INT_MAX for all nodes. vector<int> key(n, INT_MAX);
// 'mst' vector: Boolean flag indicating if a node is already included in the MST. // Initialized to false for all nodes. vector<bool> mst(n, false);
// 'parent' vector: Stores the parent of each node in the MST. 'parent[i]' is the node // from which 'i' was added to the MST. Used to reconstruct the MST edges. // Initialized to -1. vector<int> parent(n, -1);
// 3. Initialize the starting node (e.g., node 0). // The cost to include the starting node itself in the MST is 0. key[0] = 0;
// 4. Main loop of Prim's algorithm: Iterate 'n' times to include all 'n' nodes in the MST. for(int count = 0; count < n; count++) { // 'count' represents the number of nodes added to MST so far. // 4a. Find the unvisited node 'u' with the minimum 'key' value. int u = -1; // 'u' will store the index of the chosen node. int mini = INT_MAX; // 'mini' stores the minimum key value found.
for(int i = 0; i < n; i++) { // If node 'i' is not yet in the MST AND its 'key' value is smaller than 'mini'. if(mst[i] == false && key[i] < mini) { u = i; // Update 'u' to this node. mini = key[i]; // Update 'mini' to its key value. } }
// 4b. Mark the chosen node 'u' as part of the MST. mst[u] = true;
// 4c. Update 'key' and 'parent' values for neighbors of 'u'. for(pair<int, int> neighbor_pair : adjList[u]) { int neighbor_node = neighbor_pair.first; // The neighbor node ID. int edge_weight = neighbor_pair.second; // The weight of the edge from 'u' to 'neighbor_node'.
// If the neighbor is not yet in the MST AND the current edge to it is cheaper // than its existing 'key' value (i.e., we found a better way to connect it to MST). if(mst[neighbor_node] == false && edge_weight < key[neighbor_node]) { key[neighbor_node] = edge_weight; // Update 'key' to this new, smaller weight. parent[neighbor_node] = u; // Set 'u' as the parent of 'neighbor_node' in the MST. } } }
// 5. Construct the final answer: list of MST edges. vector<pair<pair<int, int>, int>> answer; // Iterate from node 1 to n-1 (node 0 is the starting node and has no parent edge in the MST). for(int i = 1; i < n; i++) { // Add the edge {parent[i], i} with its weight key[i] to the MST result. answer.push_back({{parent[i], i}, key[i]}); }
return answer; // Return the MST edges.}
int main() { vector<vector<int>> edges; // Vector to store input edges {u, v, w}. int n, m; // 'n' for nodes, 'm' for edges.
cout << "Enter the number of nodes : "; cin >> n;
cout << "Enter the number of edges : "; cin >> m;
cout << "Enter the edges (u v w, meaning u-v with weight w): " << endl; for(int i = 0; i < m; i++) { int u, v, w; cin >> u >> v >> w; // Read u, v, and weight w. edges.push_back({u, v, w}); // Add edge to vector. }
// Call calculatePrimsMST function. vector<pair<pair<int, int>, int>> mst_edges = calculatePrimsMST(edges, n, m);
cout << "Edges in the Minimum Spanning Tree (u-v : weight):" << endl; long long total_mst_weight = 0; // To calculate total MST weight. for(pair<pair<int, int>, int> edge_info : mst_edges) { int u_node = edge_info.first.first; // First node of the MST edge. int v_node = edge_info.first.second; // Second node of the MST edge. int weight = edge_info.second; // Weight of the MST edge.
cout << u_node << "-" << v_node << " : " << weight << endl; total_mst_weight += weight; } cout << "Total MST Weight: " << total_mst_weight << endl;
return 0; // Indicate successful execution.}
1. Problem Statement
- Goal: Find a Minimum Spanning Tree (MST) of a connected, undirected, weighted graph.
- MST Definition: A spanning tree is a subgraph that connects all vertices together, without any cycles, and uses exactly
V-1
edges (whereV
is the number of vertices). A Minimum Spanning Tree is a spanning tree whose sum of edge weights is as small as possible. - Input: List of weighted
edges (u, v, w)
, totaln
nodes, totalm
edges. - Output: A list of edges that form the MST, along with their weights.
2. Core Idea (Prim’s Algorithm - Greedy Approach)
Prim’s algorithm is a greedy algorithm that builds the MST one edge at a time, starting from an arbitrary initial node.
- It grows a tree from a starting vertex by iteratively adding the cheapest edge that connects a vertex already in the tree to a vertex not yet in the tree.
- It maintains three key pieces of information for each vertex
v
:key[v]
: The minimum weight of an edge connectingv
to the current MST.mst[v]
: A boolean flag indicating whetherv
has already been included in the MST.parent[v]
: The parent ofv
in the MST (i.e., the node from whichv
was added to the MST).
3. Algorithm Steps (calculatePrimsMST
function)
-
Build Adjacency List:
- Create an
adjList
(unordered_map<int, list<pair<int, int>>>
). - For each edge
(u, v, w)
:- Add
v
with weightw
toadjList[u]
. - Add
u
with weightw
toadjList[v]
(since it’s an undirected graph).
- Add
- Create an
-
Initialize Data Structures:
key
: Avector<int>
of sizen
, initialized toINT_MAX
.key[i]
will store the minimum weight to connect nodei
to the current MST.mst
: Avector<bool>
of sizen
, initialized tofalse
.mst[i] = true
means nodei
is included in the MST.parent
: Avector<int>
of sizen
, initialized to-1
.parent[i]
will store the node from whichi
was added to the MST.
-
Start Node Initialization:
- Choose an arbitrary starting node (e.g., node 0).
- Set
key[0] = 0
. (The cost to include the starting node itself is 0).
-
Main Loop (Iterate
n
times to include alln
nodes):- Loop
i
from0
ton-1
: (This loop finds and adds one node to MST in each iteration)- Find Minimum Key Node:
- Initialize
u
(node to be picked) andmini = INT_MAX
. - Iterate through all nodes
j
from0
ton-1
:- If
mst[j]
isfalse
(nodej
not yet in MST) ANDkey[j] < mini
:- Update
mini = key[j]
. - Set
u = j
.
- Update
- If
- At the end of this inner loop,
u
will be the unvisited node with the smallestkey
value.
- Initialize
- Include
u
in MST: Setmst[u] = true
. - Update Neighbors’ Keys: For each
neighbor (x.first)
withweight (x.second)
inadjList[u]
:- If
mst[x.first]
isfalse
(neighbor not yet in MST) ANDx.second < key[x.first]
:- Update
key[x.first] = x.second
. (A cheaper edge to connectx.first
to the MST is found). - Update
parent[x.first] = u
. (Nodeu
is now the best parent forx.first
).
- Update
- If
- Find Minimum Key Node:
- Loop
-
Construct MST Edges:
- Create an empty
vector<pair<pair<int, int>, int>> answer
. This will store edges as{{u, v}, weight}
. - Loop
i
from1
ton-1
(skipping the initial node 0, as it has no parent edge):- Add
{{parent[i], i}, key[i]}
toanswer
.parent[i]
is the node from whichi
was connected, andkey[i]
is the weight of that edge.
- Add
- Create an empty
-
Return: The
answer
vector, which represents the edges of the MST.
4. Time and Space Complexity
- Time Complexity:
O(V^2)
- Building
adjList
:O(M)
. - Main loop runs
V
times. - Inside the main loop:
- Finding minimum
key
node:O(V)
linear scan. - Updating neighbors:
O(degree(u))
. In total, over all iterations,sum(degree(u))
isO(E)
.
- Finding minimum
- Total:
O(V * V + E)
which simplifies toO(V^2)
becauseE
can be at mostV^2
in a dense graph.
- Building
- Space Complexity:
O(V + E)
adjList
:O(V + E)
.key
vector:O(V)
.mst
vector:O(V)
.parent
vector:O(V)
.answer
vector:O(V)
(as MST hasV-1
edges).
5. Optimizations (Using Priority Queue)
- The
O(V)
linear scan to find the minimumkey
node in each iteration can be optimized. - By using a
min-priority queue
(e.g.,std::set<pair<int, int>>
orstd::priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>>
), finding the minimum becomesO(log V)
. - This brings the time complexity down to
O(E log V)
. This is the standard competitive programming implementation for Prim’s.