Graph - Kruskal's Minimum Spanning Tree (MST)

Kruskal's algorithm finds the Minimum Spanning Tree (MST) of a connected, weighted, undirected graph: the subset of edges that connects all vertices with the minimum total edge weight and no cycles. It is a greedy algorithm that builds the MST by always picking the smallest available edge that does not form a cycle.

Contents

What is a Minimum Spanning Tree?

A Spanning Tree of a graph is a subgraph that includes all vertices and is a tree (connected and acyclic). For a graph with V vertices, a spanning tree has exactly V - 1 edges.

A Minimum Spanning Tree (MST) is the spanning tree whose total edge weight is minimised.

Weighted graph for MST

MSTs are widely used in network design problems, for example, laying cables between cities with minimum cost, designing water pipeline networks, or building computer networks.

How Kruskal's algorithm works

Kruskal's algorithm follows a greedy approach:

  1. Sort all edges in the graph by weight in ascending order.
  2. Initialise a Union-Find data structure with each vertex as its own component.
  3. Iterate through the sorted edges. For each edge (u, v):
    • If u and v belong to different components (i.e. adding this edge does not create a cycle), add it to the MST and union the two components.
    • If they already belong to the same component, skip this edge (it would create a cycle).
  4. Stop when the MST contains exactly V - 1 edges.
The key tool Kruskal's algorithm relies on is the Union-Find (Disjoint Set Union) data structure, which efficiently answers "are two nodes in the same component?" and "merge two components".

Union-Find (Disjoint Set Union)

Union-Find supports two operations efficiently:

  • find(x): returns the representative (root) of the component containing x.
  • union(x, y): merges the components containing x and y.

With path compression and union by rank, both operations run in near-constant time O(α(n)).

Java
class UnionFind {
    int[] parent, rank;

    UnionFind(int n) {
        parent = new int[n];
        rank   = new int[n];
        for (int i = 0; i < n; i++) parent[i] = i; // each node is its own root
    }

    int find(int x) {
        if (parent[x] != x) {
            parent[x] = find(parent[x]); // path compression
        }
        return parent[x];
    }

    boolean union(int x, int y) {
        int px = find(x), py = find(y);
        if (px == py) return false; // already in the same component — cycle!
        // Union by rank: attach smaller tree under larger tree
        if (rank[px] < rank[py]) { int tmp = px; px = py; py = tmp; }
        parent[py] = px;
        if (rank[px] == rank[py]) rank[px]++;
        return true;
    }
}

Implementation

The class below shows the implementation. Key points are highlighted in the inline comments.

Java
import java.util.*;

public class Kruskal {

    static class Edge implements Comparable<Edge> {
        int src, dest, weight;
        Edge(int src, int dest, int weight) {
            this.src = src; this.dest = dest; this.weight = weight;
        }
        public int compareTo(Edge other) { return this.weight - other.weight; }
    }

    /**
     * Finds the MST using Kruskal's algorithm.
     * @param V     number of vertices
     * @param edges list of all edges in the graph
     * @return list of edges in the MST
     */
    public List<Edge> kruskalMST(int V, List<Edge> edges) {
        Collections.sort(edges); // Step 1: sort edges by weight

        UnionFind uf = new UnionFind(V);
        List<Edge> mst = new ArrayList<>();

        for (Edge edge : edges) {
            if (uf.union(edge.src, edge.dest)) { // Step 3: add edge if no cycle
                mst.add(edge);
                if (mst.size() == V - 1) break; // MST complete
            }
        }
        return mst;
    }
}
Example usage:
Java
List<Kruskal.Edge> edges = new ArrayList<>();
edges.add(new Kruskal.Edge(0, 1, 4));
edges.add(new Kruskal.Edge(0, 2, 3));
edges.add(new Kruskal.Edge(1, 2, 1));
edges.add(new Kruskal.Edge(1, 3, 2));
edges.add(new Kruskal.Edge(2, 3, 4));
edges.add(new Kruskal.Edge(3, 4, 2));

Kruskal k = new Kruskal();
List<Kruskal.Edge> mst = k.kruskalMST(5, edges);

int totalWeight = 0;
for (Kruskal.Edge e : mst) {
    System.out.println(e.src + " -- " + e.dest + " : " + e.weight);
    totalWeight += e.weight;
}
System.out.println("Total MST weight: " + totalWeight);
// Output:
// 1 -- 2 : 1
// 1 -- 3 : 2
// 3 -- 4 : 2
// 0 -- 2 : 3
// Total MST weight: 8

Complexity Analysis

  • Time complexity: O(E log E). Dominated by the edge sorting step. The Union-Find operations are nearly O(1) with path compression and union by rank.
  • Space complexity: O(V + E). For the Union-Find arrays and the edge list.

Note: Kruskal's algorithm is efficient for sparse graphs (few edges). For dense graphs, Prim's algorithm may be more efficient.

Similar Articles

  1. Graph - Introduction
  2. Graph - Breadth First Search (BFS)
  3. Graph - Depth First Search (DFS)
  4. Graph - Dijkstra's shortest path algorithm
  5. Graph - Prim's minimum spanning tree algorithm