CLOSE
🛠️ Settings

Problem Statement

Given a weighted, undirected, and connected graph with V vertices numbered from 0 to V-1 and E edges.

The ith edge is represented by [a(i), b(i), w(i)], where a(i) and b(i) denotes the endpoint of the edge and the w(i) denotes the weight of the edge.

Find the sum of the weights of the edges in the Minimum Spanning Tree (MST) of the graph.

A minimum spanning tree (MST) or minimum weight spanning tree is a subset of the edges of a connected, edge-weighted undirected graph that connects all the vertices together, without any cycles and with the minimum possible total edge weight.

Examples

Example 1:

Input: V = 4, edges = [
                       [0, 1, 1],
                       [1, 2, 2],
                       [2, 3, 3],
                       [0, 3, 4]
                      ]
Output: 6

Explanation: Edges included in the MST:
[0, 1, 1] with weight 1
[1, 2, 2] with weight 2
[2, 3, 3] with weight 3
The total weight of the MST is 1 + 2 + 3 = 6. These edges form a spanning tree that connects all vertices (0, 1, 2, 3) with the minimum possible total edge weight satisfying the MST properties.
Example 2:

Input: V = 3, edges = [
                       [0, 1, 5],
                       [1, 2, 10],
                       [2, 0, 15]
                      ]
Output: 15

Explanation:
Edges included in the MST:
[0, 1, 5] with weight 5.
[1, 2, 10] with weight 10.
The total weight of the MST is 5 + 10 = 15.

Different Approaches

1️⃣ Prim's Algorithm

Intuition:

The minimum spanning tree consists of edges having minimum possible edge weight that are connecting all the nodes. So, a greedy approach can be followed where all the edges are considered from a node and the minimum edge is considered connecting that node. This way, it ensures that once all the nodes are visited, the edges that are taken into the consideration form a minimum spanning tree.
This is the principle behind the Prim's Algorithm to find the minimum spanning tree for a given graph.

Algorithm:

  1. Start with any node as the initial node in the MST. Set its key value to 0 and all other nodes' key values to infinity.
  2. Select the node with the minimum key value that is not yet included in the MST. Add this node to the MST.
  3. Update the key values of its adjacent nodes to the edge weights if the edge weight is smaller than the current key value.
  4. Repeat the process until all nodes are included in the MST.
  5. Once all the nodes are included, the MST is complete

Understanding:

Since, from every node, the minimum edge will be considered for the MST every time, the best data structure to use here will be the Minimum heap data structure, which will store a pair: {edge, node}.

Approach:

  1. A minimum priority queue (min-heap) is used to keep track of the edges based on their weights. A visited array is used to mark nodes that have been included in the MST.
  2. An arbitrary initial node is pushed into the priority queue with an edge weight of 0. While the priority queue is not empty, extract the edge with the minimum weight.
  3. If the node connected by this edge is already visited, skip it. Otherwise, mark the node as visited and add the edge weight to the MST sum.
  4. Traverse all adjacent edges of the current node. For each adjacent node that is not visited, push the edge into the priority queue.
  5. The process continues until all nodes are included in the MST. The final sum of the weights of the edges in the MST is returned.

Code:

#include <bits/stdc++.h>
using namespace std;

/* Define P as a shorthand for
the pair<int, pair<int,int>> type */
#define P pair<int,int>

class Solution{
public:

    // Function to get the sum of weights of edges in MST
    int spanningTree(int V, vector<vector<int>> adj[]) {
        
        // Min-Heap to store pair of {edge, node}
        priority_queue <P, vector<P>, greater<P>> pq;
        
        // Visited array
        vector<int> visited(V, 0);
        
        // Push any arbitrary initial node
        pq.push({0,0});
        
        // To store the weight of MST
        int sum = 0;
        
        // Until the priority queue is not empty
        while(!pq.empty()) {
            
            // Get the pair with minimum edge
            auto p = pq.top();
            pq.pop();
            
            int node = p.second; // Get the node
            int wt = p.first; // Get the edge weight
            
            /* If the node is already visited, 
            skip the iteration */
            if(visited[node] == 1) continue;
            
            // Otherwise, mark the node as visited
            visited[node] = 1;
            
            // Update the weight of MST
            sum += wt;
            
            // Traverse all the edges of the node
            for(auto it : adj[node]) {
                
                // Get the adjacent node
                int adjNode = it[0]; 
                
                // Get the edge weight
                int edgeWt = it[1];
                
                /* Add the pair to min-heap if 
                the node is not visited already */
                if(visited[adjNode] == 0) {
                    pq.push({edgeWt, adjNode});
                }
            }
        }
        
        // Return the weight of MST
        return sum;
    }
};


int main() {
    int V = 4;
    vector<vector<int>> edges = {
        {0, 1, 1},
        {1, 2, 2},
        {2, 3, 3},
        {0, 3, 4}
    };
    
    // Forming the adjacency list from edges
    vector<vector<int>> adj[4];
    for(auto it : edges) {
        int u = it[0];
        int v = it[1];
        int wt = it[2];
        
        adj[u].push_back({v, wt});
        adj[v].push_back({u, wt});
    }
    
    // Creating instance of Solution class
    Solution sol;
    
    /* Function call to get the sum 
    of weights of edges in MST */
    int ans = sol.spanningTree(V, adj);
    
    cout << "The sum of weights of edges in MST is: " << ans;
    
    return 0;
}

Complexity Analysis:

  • Time Complexity:O(ElogE) (where E is the number of edges in the graph)
    In the worst case, the min-heap will store all the E edges, and insertion operation on the min-heap takes O(logE) time taking overall O(ElogE) time.
  • Space Complexity:O(E + V) (where V is the number of nodes in the graph)
    The min-heap will store all edges in worst-case taking O(E) space and the visited array takes O(V) space.

2️⃣ Kruskal's Algorithm

Intuition:

Since, the minimum spanning tree consists of edges connecting all nodes with the minimum total weight, a greedy approach can be followed where the edges with the smallest edge weight can be added to the MST set one by one till all the nodes are not added to the MST.

Algorithm:

  1. Sort all the edges in ascending order based on their weights. Initialize the total weight sum to 0.
  2. Initialize a data structure to keep track of connected components (e.g., union-find).
  3. Iterate through the sorted edges:
    1. For each edge, check if the vertices it connects are in different components.
    2. If they are in different components, add the edge's weight to the total weight sum and unite the components.
  4. Continue this process until all vertices are connected or all edges have been processed.

Approach:

  1. A Disjoint Set class is created to manage the union-find operations. This class supports union by rank and union by size to efficiently manage the merging of sets.
  2. All edges from the adjacency list representation of the graph are extracted and stored in a list, with each edge represented as a pair of its weight and the two vertices it connects.
  3. The list of edges is sorted in ascending order based on their weights. This ensures that the smallest edges are considered first, which is a key aspect of Kruskal's algorithm.
  4. A variable is initialized to keep track of the sum of the weights of the edges included in the MST.
  5. Iterate through the sorted list of edges. For each edge, check if the two vertices connected by the edge belong to different sets using the union-find structure. If they do, add the edge's weight to the MST sum and merge the sets containing these two vertices.
  6. After processing all edges, the sum of the weights of the edges in the MST is returned.

Code:

#include <bits/stdc++.h>
using namespace std;

class DisjointSet {
    /* To store the ranks, parents and 
    sizes of different set of vertices */
    vector<int> rank, parent, size;
    
public:

    // Constructor
    DisjointSet(int n) {
        rank.resize(n + 1, 0);
        parent.resize(n + 1);
        size.resize(n + 1);
        for (int i = 0; i <= n; i++) {
            parent[i] = i;
            size[i] = 1;
        }
    }
    
    // Function to find ultimate parent
    int findUPar(int node) {
        if (node == parent[node])
            return node;
        return parent[node] = findUPar(parent[node]);
    }
    
    // Function to implement union by rank
    void unionByRank(int u, int v) {
        int ulp_u = findUPar(u);
        int ulp_v = findUPar(v);
        if (ulp_u == ulp_v) return;
        if (rank[ulp_u] < rank[ulp_v]) {
            parent[ulp_u] = ulp_v;
        }
        else if (rank[ulp_v] < rank[ulp_u]) {
            parent[ulp_v] = ulp_u;
        }
        else {
            parent[ulp_v] = ulp_u;
            rank[ulp_u]++;
        }
    }
    
    // Function to implement union by size
    void unionBySize(int u, int v) {
        int ulp_u = findUPar(u);
        int ulp_v = findUPar(v);
        if (ulp_u == ulp_v) return;
        if (size[ulp_u] < size[ulp_v]) {
            parent[ulp_u] = ulp_v;
            size[ulp_v] += size[ulp_u];
        }
        else {
            parent[ulp_v] = ulp_u;
            size[ulp_u] += size[ulp_v];
        }
    }
};

// Solution class
class Solution{
public:

    // Function to get the sum of weights of edges in MST
    int spanningTree(int V, vector<vector<int>> adj[]) {
        
        // To store the edges
        vector<pair<int, pair<int, int>>> edges;
        
        // Getting all edges from adjacency list
        for (int i = 0; i < V; i++) {
            for (auto it : adj[i]) {
                int v = it[0]; // Node v
                int wt = it[1]; // edge weight
                int u = i; // Node u
                edges.push_back({wt, {u, v}});
            }
        }
        
        // Creating a disjoint set of V vertices
        DisjointSet ds(V);
        
        // Sorting the edges based on their weights
        sort(edges.begin(), edges.end());
        
        // To store the sum of edges in MST
        int sum = 0;
        
        // Iterate on the edges
        for (auto it : edges) {
            int wt = it.first; // edge weight
            int u = it.second.first; // First node
            int v = it.second.second; // Second node
            
            // Join the nodes if not in the same set 
            if (ds.findUPar(u) != ds.findUPar(v)) {
                
                // Update the sum of edges in MST
                sum += wt;
                
                // Unite the nodes 
                ds.unionBySize(u, v);
            }
        }
        
        // Return the computed sum
        return sum;
    }
};


int main() {
    int V = 4;
    vector<vector<int>> edges = {
        {0, 1, 1},
        {1, 2, 2},
        {2, 3, 3},
        {0, 3, 4}
    };
    
    // Forming the adjacency list from edges
    vector<vector<int>> adj[4];
    for(auto it : edges) {
        int u = it[0];
        int v = it[1];
        int wt = it[2];
        
        adj[u].push_back({v, wt});
        adj[v].push_back({u, wt});
    }
    
    // Creating instance of Solution class
    Solution sol;
    
    /* Function call to get the sum 
    of weights of edges in MST */
    int ans = sol.spanningTree(V, adj);
    
    cout << "The sum of weights of edges in MST is: " << ans;
    
    return 0;
}

Complexity Analysis:

  • Time Complexity:O(V+E) + O(ElogE) + O(E*4α*2) (where V and E are the numbers of nodes and edges in the graph)
    • Extracting edges from adjacency list takes O(V+E) time.
    • Sorting the edges takes O(ElogE) time.
    • In the worst case, all the edges are iterated and added to the disjoint set. The find parent and union operation takes 4α time making overall time complexity as O(E*4α*2).
  • Space Complexity:O(V+E)
    • Storing the edges take O(E) space.
    • The disjoint set on V vertices take O(V) space due to parent, rank/size arrays.