Skip to content

Expert: advanced patterns and interviews · Tries, union-find and bit manipulation · topic 49 of 61 · 35 min

Minimum spanning trees

Explanation

A minimum spanning tree (MST) connects all the vertices of a weighted, undirected graph using the cheapest possible set of edges, with no cycles: exactly V − 1 edges. Kruskal's algorithm sorts the edges and adds each one that joins two different union-find groups. Prim's algorithm grows one tree from a start vertex using a heap. Both run in about O(E log E).

The problem: connect everything cheaply

A district wants to link all its villages by road. Many roads are possible, each with a building cost. Which roads should it build so that every village can reach every other, at the lowest total cost? The same question appears when laying fibre-optic cable between offices, electricity lines between towns, or water pipes in a housing society.

In graph words: the graph is undirected and weighted. A spanning tree is a set of edges that connects all V vertices with no cycle; it always has exactly V − 1 edges. A minimum spanning tree (MST) is a spanning tree with the smallest total weight. If the graph is disconnected, no spanning tree exists. There may be several MSTs with the same total when weights repeat.

Careful: An MST is not a shortest-path tree. It minimises the total cost of the whole network. The route between two particular villages along the MST can be much longer than their shortest path (topic 5 of Module 9).

Kruskal's algorithm

  1. Sort all edges by weight, cheapest first.
  2. Go through them in order. If an edge joins two vertices that are in DIFFERENT union-find groups, take it and union the groups.
  3. If both ends are already in the same group, skip the edge: it would close a cycle.
  4. Stop after taking V − 1 edges. If you run out of edges first, the graph is disconnected.

Why is the greedy choice safe? Split the vertices into any two sides. The cheapest edge that crosses between the sides always belongs to some MST (this is called the cut property). Each edge Kruskal takes is the cheapest edge joining its group to the rest, so it is always safe.

python
def kruskal(n, edges):
    parent = list(range(n))

    def find(x):
        while parent[x] != x:
            parent[x] = parent[parent[x]]   # path halving
            x = parent[x]
        return x

    chosen = []
    for w, u, v in sorted((w, u, v) for u, v, w in edges):
        ru, rv = find(u), find(v)
        if ru != rv:
            parent[ru] = rv
            chosen.append((u, v, w))
    return chosen

edges = [(0, 1, 4), (0, 2, 1), (1, 2, 2), (1, 3, 5), (2, 3, 8)]
print(kruskal(4, edges))   # Output: [(0, 2, 1), (1, 2, 2), (1, 3, 5)]
Edge 0–1 (4) is skipped because 0 and 1 are already joined through 2. Total 1 + 2 + 5 = 8.

Sorting costs O(E log E), and the union-find work is almost O(1) per edge, so Kruskal is O(E log E) time and O(V + E) space. Note that E log E and E log V are the same order, since E is at most V².

Prim's algorithm

Prim grows ONE tree. Start with any vertex. Keep a min-heap of edges that leave the tree. Pop the cheapest; if it leads to a vertex already in the tree, skip it; otherwise add that vertex, add the edge's weight to the total, and push all its edges. It looks like Dijkstra, but the heap key is the weight of the single edge, not the total distance from the start.

python
import heapq

def prim(n, adj, start=0):
    in_tree = [False] * n
    heap = [(0, start)]
    total = 0
    added = 0
    while heap and added < n:
        w, u = heapq.heappop(heap)
        if in_tree[u]:
            continue
        in_tree[u] = True
        total += w
        added += 1
        for v, wv in adj[u]:
            if not in_tree[v]:
                heapq.heappush(heap, (wv, v))
    return total if added == n else -1

adj = [[(1, 4), (2, 1)], [(0, 4), (2, 2), (3, 5)], [(0, 1), (1, 2), (3, 8)], [(1, 5), (2, 8)]]
print(prim(4, adj))   # Output: 8
The same graph as above; Prim reaches the same total. O(E log V) time with a heap.

Which one to use

SituationGood choiceWhy
Input is an edge list, graph is sparseKruskalSort once, then cheap union-find checks
Input is an adjacency listPrim with a heapOnly looks at edges next to the tree
Every pair of points can be joined (complete graph)Prim with a plain array, O(V²)There are about V²/2 edges, so sorting them all is wasteful

The "connect all points" problem is the last case: every pair of points can be joined, at a cost equal to their Manhattan distance |x1 − x2| + |y1 − y2|. Keep, for every vertex not yet in the tree, the cheapest edge to the tree, and each round pick the smallest. That is O(V²) time and O(V) space, with no heap and no edge list.

Common mistakes

  • Not checking that the result has V − 1 edges, so a disconnected graph gets a wrong "answer". Fix: count the edges taken (or vertices added).
  • Using an MST to answer "shortest route from A to B". Fix: that is Dijkstra.
  • In Prim, marking a vertex as in the tree when it is pushed instead of when it is popped. Fix: mark it when popped, like Dijkstra.
  • Sorting edges by the wrong field, for example (u, v, w) tuples sorted by u. Fix: sort by weight, with key=lambda e: e[2] or by putting w first.
  • Applying it to a directed graph. Fix: the MST is defined for undirected graphs; directed versions need different algorithms.

Need help?

Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.

Try:

Watch the lesson

Minimum spanning trees · 1/8

Minimum spanning trees

This lesson is about minimum spanning trees: connecting every vertex for the lowest total cost.

Prefer a video by a teacher? Find free videos on “minimum spanning tree kruskal prim algorithm explained” on YouTube ↗

In real life: Laying fibre between offices

A company wants to connect its five offices in a city with its own fibre-optic cable. The network team estimates the cost of each possible cable route (in lakh rupees, made-up numbers). Any office must be able to reach any other, directly or through other offices, and the total cost should be as low as possible. Kruskal's algorithm picks the cables. Real planning also adds backup links for safety, which an MST alone does not give you.

python
routes = [("HQ", "Tech Park", 4), ("HQ", "Warehouse", 7), ("Tech Park", "Warehouse", 3),
          ("Tech Park", "Sales", 6), ("Warehouse", "Sales", 5), ("Sales", "Support", 2),
          ("HQ", "Support", 9)]
parent = {}

def find(x):
    parent.setdefault(x, x)
    while parent[x] != x:
        x = parent[x]
    return x

total = 0
for a, b, cost in sorted(routes, key=lambda r: r[2]):
    ra, rb = find(a), find(b)
    if ra != rb:
        parent[ra] = rb
        total += cost
        print("lay", a, "-", b, cost)
print("total", total)
Lays Sales–Support (2), Tech Park–Warehouse (3), HQ–Tech Park (4) and Warehouse–Sales (5), for a total of 14.

Practice (0/3 done)

Task 1 · Code · Python

Write kruskal_mst_cost(n, edges) for an UNDIRECTED graph with vertices 0 to n − 1 and edges (u, v, w). Weights can be negative, and there may be parallel edges or self-loops. Return the total weight of a minimum spanning tree, or -1 if the graph is not connected. With 0 or 1 vertices the cost is 0. Use sorting plus union-find.

Task 2 · Code · Python

points is a list of [x, y] integer points. Joining two points costs their Manhattan distance |x1 − x2| + |y1 − y2|. Write min_cost_connect_points(points) that returns the minimum total cost to connect all points (any point can then reach any other, directly or through others). An empty list or a single point costs 0. Inputs are small (up to about 300 points), so Prim's O(n²) array version is ideal.

Task 3 · Code · Python

There are n villages labelled 0 to n − 1. existing is a list of pairs (u, v) for roads that are ALREADY built (cost 0). proposals is a list of (u, v, cost) for roads that could be built. Write connect_villages(n, existing, proposals) that returns the minimum extra cost so that every village can reach every other, or -1 if that is impossible. Hint: union the existing roads first, then run Kruskal on the proposals.

Quick quiz

Question 1

1.How many edges does a spanning tree of a connected graph with 10 vertices have?

Question 2

2.In Kruskal's algorithm, the next cheapest edge joins two vertices already in the same group. What do you do?

Question 3

3.Which statement about an MST is true?

Question 4

4.You must connect 1,000 points where every pair can be joined. Which approach is natural?

Question 5

5.What is the time of Kruskal's algorithm with union-find?

Read more (free)

Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.

How many edges does a spanning tree of a connected graph with 10 vertices have?

Answer: 9

A tree on V vertices always has V − 1 edges.

In Kruskal's algorithm, the next cheapest edge joins two vertices already in the same group. What do you do?

Answer: Skip it, because it would close a cycle

Both ends are already connected, so the edge adds nothing but a cycle.

Which statement about an MST is true?

Answer: It minimises the total weight of the edges that connect everything

It minimises the sum over the whole tree; individual routes along it can be long.

You must connect 1,000 points where every pair can be joined. Which approach is natural?

Answer: Prim with a plain array, O(V²)

In a complete graph, Prim with an array avoids building and sorting about V²/2 edges.

What is the time of Kruskal's algorithm with union-find?

Answer: O(E log E)

Sorting the edges dominates; union-find adds almost O(1) per edge.

An undirected graph has 5 edges. What do all the degrees add up to?Related

Answer: 10

Every edge touches two vertices, so it adds 2 to the total: 2 × 5 = 10.

From Graphs: vertices, edges and how to store them

A graph has a few negative edges but no negative cycle. Which algorithm should you use for one source?Related

Answer: Bellman-Ford

Bellman-Ford allows negative edges; Dijkstra may settle a vertex too early.

From Shortest paths: Dijkstra and friends

Kahn's algorithm outputs 4 vertices of a 6-vertex graph. What does that tell you?Related

Answer: The graph has a cycle

Vertices on a cycle never reach in-degree 0, so they are left out.

From Topological sort

Directed edges 0→1, 0→2, 1→3, 2→3. A DFS from 0 meets vertex 3 a second time. Is there a cycle?Related

Answer: No: 3 is finished (black), not on the current path

Only an edge to a grey vertex (on the current path) closes a cycle. This is a diamond.

From Depth-first search (DFS)

Pass the quiz (60% or more) or mark this topic as done when you are ready.

Your progress is saved on this device only.

Next steps

  1. 2 more topics in “Tries, union-find and bit manipulation” before its exam: Bits: AND, OR, XOR and shifts, Bit tricks in interviews.
  2. Module 10 exam: Tries, union-find and bits · 14 questions · 25 min, without help. 5 topics of this module still to finish first.
  3. Then the next module, Dynamic programming. 61 topics left in the course before the final exam.

← Previous: Union-find (disjoint set union)