Graph Theory & Greedy Algorithms

Kruskal's MST Algorithm Explained

Kruskal's algorithm sorts every edge by weight and keeps each one unless it would close a cycle, holding a forest of fragments that only becomes a tree at the very end. Learn the cycle property that makes rejecting an edge provably safe, follow a worked six-vertex trace, and see why union-find is what makes it fast.

12 Min Read Updated: August 2026 Advanced Level
Mohammed Islam Hadjoudj
Mohammed Islam Hadjoudj
Expert Operations Research Engineer

1. Introduction to Kruskal's Algorithm

Kruskal's algorithm builds a minimum spanning tree: from a connected, undirected, weighted graph it selects the cheapest set of edges that touches every vertex without forming a cycle. For V vertices that set always contains exactly V - 1 edges.

Where Prim's algorithm grows one connected tree outward from a starting vertex, Kruskal's ignores connectivity almost entirely until the end. It sorts every edge in the graph by weight, walks that list from cheapest to most expensive, and keeps each edge unless doing so would close a cycle. Nothing else is consulted. There is no start vertex and no notion of a frontier.

The consequence is that Kruskal's spends most of its run holding a forest rather than a tree: many small fragments scattered across the graph, growing and merging independently, that only fuse into a single spanning tree with the very last edge it accepts. That difference in shape is what makes it behave differently on sparse graphs, on disconnected input, and under a profiler.

2. Why It Works: The Cycle Property

Prim's correctness rests on a theorem about which edges are safe to accept. Kruskal's spends just as much time rejecting edges, so it is worth stating the companion theorem that justifies throwing one away.

The cycle property. For any cycle in the graph, the maximum-weight edge on that cycle does not belong to the minimum spanning tree, provided it is strictly heavier than every other edge on the cycle. If several edges tie for heaviest, at least one of them can be left out.

The proof mirrors the one for the cut property. Suppose the heaviest edge e on some cycle did belong to a minimum spanning tree T. Removing e from T splits it into two components. The rest of that cycle still runs between those two components, so some other cycle edge f reconnects them. Put f back instead. You have a spanning tree again, and since e was strictly the heaviest, the new tree is strictly lighter, contradicting the assumption that T was minimum.

Now look at what happens when Kruskal's rejects an edge. It reaches edge e and finds that both endpoints already sit in the same fragment. That means a path between them already exists, built entirely from edges the algorithm accepted earlier, which means from edges no heavier than e. Adding e would close a cycle on which e is the heaviest member. By the cycle property, discarding it costs nothing.

The accepted edges are safe for the same reason Prim's are. When an edge joins two different fragments, it is the cheapest remaining edge crossing the cut that separates one fragment from everything else, so the cut property applies unchanged. Kruskal's is greedy in both directions at once, and both directions are theorems.

The cycle A-C-B-A in the example graph, with weights 1, 2 and 4. The heaviest edge on the cycle, A-B at weight 4, is marked as excluded from the minimum spanning tree, while the two lighter cycle edges are kept.
A preview of the six-vertex example graph introduced in section 4. On the cycle A-C-B-A, the heaviest edge is A-B at 4, and the cycle property says it can be left out.

3. How the Forest Merges

Concretely, the algorithm keeps every vertex in its own fragment to begin with: V fragments, zero edges. Processing the sorted edge list, it asks one question of each edge (u, v):

Each accepted edge reduces the fragment count by exactly one. Starting at V fragments and finishing at one means precisely V - 1 acceptances, which is the stopping condition. Once you have that many, every remaining edge in the list is guaranteed to be rejected, so the loop can exit early.

The whole algorithm therefore reduces to a single data-structure question: how do you test "same fragment?" and "merge these two fragments" quickly, millions of times? That is exactly what union-find provides.

A weighted undirected graph with six nodes A to F. The minimum spanning tree is highlighted using edges A-C weight 1, B-C weight 2, D-E weight 2, E-F weight 3 and B-D weight 5, connecting all six nodes for a total weight of 13. The heavier edges A-B, C-D, C-E and D-F are left unused.
A preview of the six-vertex example graph introduced in section 4, with the tree Kruskal will build highlighted: five edges, total weight 13.

4. Step-by-Step Execution

Take the six-vertex graph used throughout our minimum spanning trees guide. Its nine edges are:

A-B 4    A-C 1    B-C 2
B-D 5    C-D 6    C-E 7
D-E 2    D-F 8    E-F 3

Sorting them by weight gives the order the algorithm will actually walk:

A-C 1   B-C 2   D-E 2   E-F 3   A-B 4   B-D 5   C-D 6   C-E 7   D-F 8

Every vertex starts alone, so the forest begins as {A} {B} {C} {D} {E} {F}.

  1. A-C (1). Accept. Different fragments. Forest becomes {AC} {B} {D} {E} {F}. Running total 1.
  2. B-C (2). Accept. B is alone, C is with A. Forest becomes {ABC} {D} {E} {F}. Total 3.
  3. D-E (2). Accept. Note this builds a fragment nowhere near the first one. Forest becomes {ABC} {DE} {F}. Total 5.
  4. E-F (3). Accept. Forest becomes {ABC} {DEF}. Total 8.
  5. A-B (4). Reject. A and B are both already in {ABC}, so this would close the cycle A-C-B-A. Discarded.
  6. B-D (5). Accept. This bridges the two remaining fragments. Forest becomes {ABCDEF}. Total 13.

Five edges accepted for six vertices, so the algorithm stops without ever examining C-D (6), C-E (7) or D-F (8). The resulting tree is A-C (1), B-C (2), D-E (2), E-F (3), B-D (5), with total weight 13, exactly the tree Prim's finds on the same graph.

Step 3 is the moment that distinguishes this algorithm. Prim's could not have taken D-E at that point, because neither endpoint touched its growing tree. Kruskal's does not care: it happily starts a second, unrelated fragment on the other side of the graph and worries about connecting them later. And step 5 shows the cycle property in action, rejecting exactly the edge that is heaviest on the cycle it would have created.

The forest maintained by Kruskal's algorithm across its six decisions. It begins as six single-vertex fragments and merges step by step: AC, then ABC, then a separate DE, then DEF, with A-B rejected, and finally B-D joining the two halves into one tree of weight 13.
Kruskal's holds a forest, not a tree. Fragments grow independently and only fuse into one spanning tree with the last accepted edge.

5. Union-Find: The Structure That Makes It Fast

The naive way to test whether two vertices are already connected is to run a traversal from one and see whether you reach the other. That costs O(V) per edge and drags the whole algorithm to O(V * E), which is worse than the sort it was supposed to complement.

Union-find, also called the disjoint-set union structure, answers both questions in near-constant time. It maintains each fragment as a tree of parent pointers with one representative element at the root, and exposes two operations:

Two optimisations make it fast enough to disappear from the complexity analysis. Union by rank always attaches the shorter tree under the taller one, keeping the structure from degenerating into a chain. Path compression re-points every node visited during a find straight at the root, so repeated lookups get cheaper as the algorithm runs.

With both applied, a sequence of m operations on n elements costs O(m α(n)), where α is the inverse Ackermann function. It grows so slowly that it is below 5 for any input that fits in the observable universe, so the union-find work in Kruskal's is effectively linear in the number of edges.

6. Implementation and Pseudocode

Because union-find carries the difficulty, the algorithm itself is short.

function Kruskal(V, edges):
    sort edges by weight, ascending
    makeSet(v) for every vertex v      // V singleton fragments
    mst = []

    for each edge (u, v, w) in sorted order:
        if find(u) != find(v):         // different fragments
            union(u, v)
            mst.append((u, v, w))
            if size(mst) == V - 1:     // early exit: tree is complete
                break

    return mst

Two details are worth defending. The break is not required for correctness, since every later edge would be rejected anyway, but on a dense graph it skips most of the list. And dropping the break entirely is not a bug: it is what turns this into a minimum spanning forest algorithm, which is discussed below.

The comparison find(u) != find(v) is the only place cycles are ever considered. There is no explicit cycle detection anywhere, which is the elegance of the approach: connectivity bookkeeping does the job implicitly.

Kruskal's algorithm on the example graph. The edges are sorted by weight and considered cheapest first: A-C, B-C, D-E and E-F are added, A-B of weight 4 is skipped because it would form a cycle, and B-D is added to finish the tree. Total weight is 13.
What that pseudocode produces, seen on the graph: the sorted edges walked cheapest first, with only A-B rejected.

7. Time and Space Complexity

Because the cost is concentrated in one place, the useful optimisations all attack the sort. If the edges arrive pre-sorted, or the weights are small integers that permit a radix or counting sort, the whole algorithm drops to O(E α(V)), which is very close to linear. A partial sort or a lazy heap also helps: you rarely need the full ordering, because the algorithm usually stops long before reaching the heaviest edges.

8. Kruskal's vs Prim's

Both are greedy, both are justified by the same pair of theorems, and on a graph with distinct weights both return the identical tree. The practical differences follow from what each keeps connected.

                 Kruskal                      Prim
structure        forest of fragments          one growing tree
driven by        a sorted edge list           a priority queue
needs            union-find                   a heap (or a V x V scan)
cost             O(E log E)                   O(E log V), or O(V^2) dense
best on          sparse graphs                dense graphs
disconnected     gives a spanning forest      spans one component only

The rule of thumb is density. On a sparse graph, E is small, the sort is cheap, and Kruskal's wins. On a dense graph where E approaches V2, sorting roughly V2 edges costs O(V2 log V), while Prim's with an adjacency matrix runs in a flat O(V2) and takes the lead.

Side by side comparison of Prim's and Kruskal's on the same six-vertex graph. Prim's grows one connected tree outward from A in the order A, C, B, D, E, F. Kruskal's builds several disconnected fragments that merge at the end. Both produce a tree of total weight 13.
Two routes to the same answer. Prim keeps one tree connected throughout; Kruskal lets fragments form anywhere and merges them last.

9. Practical Notes and Common Pitfalls

Spanning Forests and Disconnected Graphs

This is where Kruskal's has a genuine structural advantage. Run it on a graph that is not connected and it simply never reaches V - 1 accepted edges. It exhausts the edge list, and what remains is a minimum spanning forest: the minimum spanning tree of each connected component, computed in one pass with no special handling.

Prim's cannot do this from a single start. Launched at one vertex, it spans that vertex's component and stops, silently returning a tree that looks valid but covers only part of the graph. Recovering the rest means detecting the shortfall and restarting from an unvisited vertex, once per component.

The count is also the diagnostic. If Kruskal's finishes having accepted V - 1 edges the graph was connected; if it accepted V - k, the graph had k components. Connectivity information falls out of the algorithm for free.

Maximum Spanning Tree

Sort descending instead of ascending and every line of the algorithm still applies. The cycle property flips to a cut property argument on negated weights, and you get the heaviest spanning tree. Negating the weights and running the algorithm unchanged works equally well.

Reverse-Delete

The mirror image of Kruskal's: sort edges from heaviest to lightest and delete each one unless deleting it would disconnect the graph. It is justified by the same cycle property read backwards, and it produces the same tree. It is rarely used because the connectivity test after each deletion is far more expensive than a union-find query.

Tied Weights

When several edges share a weight the graph can have more than one minimum spanning tree, and which one you get depends on how your sort orders the ties. The example above contains such a tie: B-C and D-E both weigh 2. All the resulting trees are equally optimal, so any test that compares against a fixed edge list is fragile. Compare total weight instead.

Self-Loops and Parallel Edges

A self-loop always fails the find(u) != find(v) test and is discarded automatically, so it needs no special handling. Among parallel edges, the cheapest is reached first and accepted, and the rest are then rejected as cycles. Kruskal's is unusually forgiving about messy input.

10. Real-World Applications

Network and Infrastructure Design

The original motivation: connect a fixed set of sites with the least total cable, pipe or track. Kruskal's edge-centric view fits naturally when the input is already a list of candidate links with costs, which is how such data usually arrives.

Hierarchical Clustering

Run Kruskal's and record the order in which fragments merge, and you have performed single-linkage agglomerative clustering. The sequence of merges is precisely the dendrogram, and stopping early at V - k edges leaves exactly k clusters. This equivalence is why minimum spanning trees show up so often in unsupervised learning.

Image Segmentation

Treating pixels as vertices and intensity differences as edge weights, the Felzenszwalb-Huttenlocher segmentation algorithm is essentially Kruskal's with a merge predicate that compares internal fragment variation against the edge weight.

Circuit and Layout Design

Minimising total wire length between fixed pads is an MST problem, and the edge list of candidate routes is naturally what a router already has to hand.

11. Academic Resources and History

Unlike Prim's algorithm, which was discovered independently at least three times, this one has a clean attribution.

For the full attribution history of the problem, see Graham and Hell. For rigorous proofs of the cut and cycle properties and of both algorithms, the standard reference is Cormen, Leiserson, Rivest and Stein, Introduction to Algorithms, in the chapter on minimum spanning trees. Full citations appear at the end of this article.

Frequently Asked Questions

Why is it safe for Kruskal's algorithm to reject an edge?

Because of the cycle property: the heaviest edge on any cycle can be left out of the minimum spanning tree. When Kruskal's rejects an edge, both endpoints are already in the same fragment, which means a path between them already exists built from edges it accepted earlier, and therefore from edges no heavier than this one. The rejected edge is the heaviest on the cycle it would have closed, so discarding it costs nothing.

Why does Kruskal's algorithm need union-find?

The algorithm must ask, for every edge, whether its two endpoints are already connected. Answering that with a graph traversal costs O(V) per edge and would dominate the whole running time. Union-find answers it in near-constant time using union by rank and path compression, giving O(E alpha(V)) for all the connectivity work, where alpha is the inverse Ackermann function and is below 5 for any practical input.

When should I use Kruskal's algorithm instead of Prim's?

Prefer Kruskal's on sparse graphs, when the input already arrives as an edge list, or when the graph may be disconnected. Its cost is dominated by sorting at O(E log E), which is cheap when E is small, and on a disconnected graph it naturally produces a minimum spanning forest in one pass. Prefer Prim's on dense graphs, where an adjacency-matrix implementation runs in a flat O(V^2) and avoids sorting roughly V squared edges.

Watch Kruskal accept and reject edges

Sort the edges, then watch union-find wave each one through or turn it away. Run Kruskal on a live graph, step by step.

Open the Kruskal Visualizer

Verified References & Further Reading