Skip to content
DSA graphs 8 min read

Minimum Spanning Tree: Prim's and Kruskal's Algorithms

A Minimum Spanning Tree (MST) of a connected, weighted, undirected graph is a subset of edges that connects all V vertices with no cycle and the smallest possible total weight. It always uses exactly V - 1 edges. MSTs answer “what’s the cheapest way to wire up every node?” — network design, clustering, road/cable layout. Two greedy algorithms find it: Prim’s grows a tree outward, Kruskal’s adds the cheapest safe edges globally.

Prim’s algorithm (grow with a min-heap)

Prim’s starts from any vertex and repeatedly adds the cheapest edge that reaches a new vertex, using a priority queue keyed by edge weight — structurally similar to Dijkstra.

#include <vector>
#include <queue>

// graph[u] = list of (neighbor, weight); returns total MST weight
long long primMST(const std::vector<std::vector<std::pair<int,int>>>& graph) {
    int n = graph.size();
    std::vector<bool> inMST(n, false);
    using P = std::pair<int,int>;                  // (weight, vertex)
    std::priority_queue<P, std::vector<P>, std::greater<P>> pq;
    pq.push({0, 0});
    long long total = 0; int taken = 0;
    while (!pq.empty() && taken < n) {
        auto [w, u] = pq.top(); pq.pop();
        if (inMST[u]) continue;                    // skip settled vertex
        inMST[u] = true; total += w; taken++;
        for (auto [v, wt] : graph[u])
            if (!inMST[v]) pq.push({wt, v});
    }
    return total;
}

Pitfall: A vertex can sit in the heap multiple times with different weights. Check inMST[u] right after popping and skip if already included — otherwise you double-count.

Kruskal’s algorithm (sort edges + union-find)

Kruskal’s takes the opposite view: sort all edges by weight, then add each edge whose endpoints aren’t already connected. “Already connected?” is answered in near-constant time by union-find. Adding an edge within the same set would create a cycle, so it’s skipped.

struct DSU {
    std::vector<int> p, r;
    DSU(int n): p(n), r(n, 0) { for (int i=0;i<n;i++) p[i]=i; }
    int find(int x){ return p[x]==x ? x : p[x]=find(p[x]); }
    bool unite(int a,int b){ a=find(a); b=find(b);
        if(a==b) return false;
        if(r[a]<r[b]) std::swap(a,b);
        p[b]=a; if(r[a]==r[b]) r[a]++; return true; }
};
// edges: (weight, u, v); returns total MST weight
long long kruskalMST(int V, std::vector<std::array<int,3>> edges) {
    std::sort(edges.begin(), edges.end());         // by weight (index 0)
    DSU dsu(V);
    long long total = 0; int taken = 0;
    for (auto& [w, u, v] : edges) {
        if (dsu.unite(u, v)) { total += w; if (++taken == V-1) break; }
    }
    return total;
}

Complexity and choosing one

Algorithm   Time                  Best for
Prim's      O((V + E) log V)      dense graphs (adjacency list + heap)
Kruskal's   O(E log E)            sparse graphs / edge list

Both produce a tree of the same total weight (the MST weight is unique when edge weights are distinct). Pick Prim’s when the graph is given as an adjacency list and is dense; pick Kruskal’s when you have an edge list or the graph is sparse — and it leans entirely on union-find.

Ready to test yourself? Head to the graph exercises.

Last updated June 25, 2026
Was this helpful?