Connecting the whole network
Suppose several locations must be connected by cables. Paying separately for a short route from one chosen location to every other location is not necessarily the right objective. If the requirement is only that every location can reach every other location, shared links may reduce the total construction cost. This is the minimum-spanning-tree problem.
The distinction between local and global cost is the central difficulty. A cheap edge may be useful, but adding every cheap edge can create a cycle. A cycle contains redundancy: removing one edge from it leaves its vertices connected. Prim's and Kruskal's algorithms are greedy, yet they do not merely say “take the cheapest edge.” Each algorithm specifies which edges are currently eligible and rejects edges that would destroy the tree structure.
Throughout this note, let G=(V,E) be a finite, nonempty, connected,
undirected graph with a real weight w(e) on each edge. Connectivity is
essential: a disconnected graph has no tree spanning all vertices. The weights
need not be distinct and need not be nonnegative. Equal weights can produce
several different MSTs with the same minimum total weight.
Trees, cuts, and safe edges
Definition 5.2.1
(Tree and spanning tree)
A tree is a connected undirected graph with no cycle. A spanning tree
of G=(V,E) is a subgraph T=(V,E_T) that uses every vertex of G and is a
tree. Thus E_T is a subset of E, but the vertex set is unchanged.
For a one-vertex graph, the empty edge set is its spanning tree. If G is
disconnected, no spanning tree exists; one can instead discuss a spanning
forest, with one tree for each connected component.
Definition 5.2.2
(Minimum spanning tree)
The weight of a spanning tree T is
A minimum spanning tree (MST) is a spanning tree whose total weight is no
larger than that of any other spanning tree of G. “Minimum” refers to the sum
of the selected edge weights, not to the number of edges: every spanning tree
already has the same number of edges.
Definition 5.2.3
(Cut, crossing edge, and safe edge)
A cut partitions V into two nonempty sets S and V\setminus S. An edge
crosses the cut if one endpoint is in S and the other is outside S. A cut
respects a currently chosen forest F if no edge of F crosses it.
An edge is safe for F if adding it keeps open the possibility that the
chosen edges are contained in some MST. Safety is stronger than merely “does
not make a cycle”: it connects cycle avoidance to global optimality.
An MST is also different from a shortest-path tree. After choosing a source
s, a shortest-path tree preserves a shortest route from s to every
reachable vertex. An MST has no distinguished source and minimizes the sum of
tree edges. Consequently, paths inside an MST need not be shortest paths in the
original graph.
Worked example 5.2.4
(MST is not a shortest-path tree)
Consider the triangle with edges s-a of weight 2, s-b of weight 2, and
a-b of weight 1. An MST chooses a-b and either s-a or s-b, for total
weight 3.
If the source is s, however, a shortest-path tree must use both weight-2
edges. The direct distances to a and b are both 2, whereas going through
the other non-source vertex costs 3. That shortest-path tree has edge-weight
sum 4. It solves the source-distance objective, while the MST solves the
total-connection objective.
Safe greedy choices
Theorem 5.2.5
(Edge count of a tree)
Every tree with vertex set V has exactly |V|-1 edges. Therefore a spanning
tree is complete as soon as it is connected, acyclic, and has |V|-1 selected
edges. Conversely, a connected undirected graph with |V|-1 edges is a tree.
This fact gives both algorithms a precise stopping condition. Before |V|-1
accepted edges, the construction is still a forest. Accepting more than
|V|-1 edges would force a cycle.
Theorem 5.2.6
(Light crossing-edge safety principle)
Let F be a forest contained in some MST, and let a cut respect F. If e is
a minimum-weight edge among all edges crossing that cut, then e is safe for
F: there is an MST containing F together with e.
Prim applies the principle to the cut between the vertices already in its one growing tree and all remaining vertices. Kruskal applies it to a cut around one component of its current forest. This is why their different eligibility rules lead to the same kind of optimum.
Why a light crossing edge is safe
For the edge-count statement, use induction on the number of vertices. A
one-vertex tree has zero edges. Every finite tree with at least two vertices
has a leaf. Remove a leaf and its unique incident edge. The remaining graph is
still a tree, now on one fewer vertex, so by induction it has |V|-2 edges.
Restoring the removed edge gives |V|-1.
For the safe-edge principle, assume an MST M contains F. If M already
contains e, nothing must be changed. Otherwise add e to M. Because M
is a tree, this creates exactly one cycle. The cycle enters and leaves the two
sides of the cut, so it contains another crossing edge f. The cut respects
F, hence f is not an edge of F. Since e is a lightest crossing edge,
w(e) ≤ w(f). Replace f by e. The result is again a spanning tree, it
still contains F, and its weight did not increase. Because M was already
minimum, the replacement is also an MST. This exchange argument proves that
the greedy choice can be extended to an optimum.
For Prim, every accepted edge has exactly one endpoint in the current tree, so
it crosses the relevant cut and cannot create a cycle. For Kruskal, consider a
candidate (u,v) whose endpoints lie in different forest components. The cut
around u's component respects the forest. Processing edges in nondecreasing
weight order makes the candidate a lightest still-relevant crossing edge, so
the exchange argument applies.
Prim's algorithm
Prim maintains a set S of vertices already in the tree. It repeatedly accepts
the cheapest edge with exactly one endpoint in S. An edge priority queue
keeps these candidates ordered by weight; an entry becomes stale if its outside
endpoint joins the tree through another edge.
PRIM_WITH_EDGE_QUEUE(G, start):
inTree[v] ← false for every vertex v
T ← empty edge set
pq ← empty min-priority queue of (weight, from, to)
inTree[start] ← true
push every edge (start, x) with x not inTree into pq
while pq is not empty and |T| ≠ |V| - 1:
(weight, u, v) ← pq.popMin()
if inTree[v]:
continue // stale edge; both endpoints are in S
add (u, v) to T
inTree[v] ← true
for each edge (v, x):
if not inTree[x]:
pq.push(weight(v,x), v, x)
if |T| != |V| - 1: report "disconnected"
otherwise return T
Worked example 5.2.7
(Complete Prim trace)
The graph has vertices A,B,C,D,E and weighted edges
AB=1, BD=2, BC=3, AD=5, DE=6, DC=7, CE=8.
Start at A. The states below show the accepted tree edges and the useful
priority-queue information.
| Step | Current vertices S | Queue action and decision | Accepted edges |
|---|---|---|---|
| 0 | {A} | insert AB(1), AD(5) | none |
| 1 | {A,B} | pop AB(1); insert BD(2), BC(3) | AB |
| 2 | {A,B,D} | pop BD(2); insert DE(6), DC(7) | AB, BD |
| 3 | {A,B,C,D} | pop BC(3); insert CE(8) | AB, BD, BC |
| 4a | unchanged | pop AD(5) and reject it: both endpoints are already in S | unchanged |
| 4b | all five vertices | pop DE(6) and accept it | AB, BD, BC, DE |
The selected tree has four edges, as required for five vertices, and total
weight 1+2+3+6=12. The rejection of AD is not optional housekeeping. It is
the cycle-avoidance step for an edge-priority-queue implementation: accepting
AD would close the cycle A-B-D-A.
With an adjacency list, each edge is inspected when one endpoint joins the
tree and at most O(E) queue entries are pushed and popped. A binary heap with
up to O(E) entries therefore gives O(E log E) time and O(V+E) space. For
a simple graph, log E=O(log V), so the same bound is often written
O(E log V). This analysis belongs specifically to the edge-queue version
above; an adjacency-matrix implementation that repeatedly scans all vertices
instead has O(V^2) time.
Kruskal's algorithm
Kruskal does not grow from a start vertex. It begins with every vertex as a separate component, sorts all edges by nondecreasing weight, and accepts an edge exactly when its endpoints are currently in different components.
A DFS or BFS in the current forest determines whether the endpoints are already connected. The following implementation uses this test without an additional component data structure.
KRUSKAL_WITH_FOREST_SEARCH(G):
edges ← all edges sorted by nondecreasing weight
F ← graph with all vertices and no edges
if F already has |V| - 1 edges:
return F // handles the one-vertex graph
for each (u, v) in edges:
if HAS_PATH_BY_DFS_OR_BFS(F, u, v):
continue // adding (u,v) would form a cycle
add (u, v) to F
if F has |V| - 1 edges:
return F
report "disconnected"
Worked example 5.2.8
(Complete Kruskal trace on the same graph)
The sorted order is
AB(1), BD(2), BC(3), AD(5), DE(6), DC(7), CE(8).
| Edge considered | Endpoint state before decision | Decision | Forest after decision |
|---|---|---|---|
AB(1) | A and B separate | accept | {AB} |
BD(2) | B and D separate | accept | {AB,BD} |
BC(3) | B and C separate | accept | {AB,BD,BC} |
AD(5) | path A-B-D already exists | reject: it would form A-B-D-A | unchanged |
DE(6) | E is separate from the other component | accept and stop | {AB,BD,BC,DE} |
Again the total is 12. Edges DC(7) and CE(8) need not be processed once
four edges have been accepted. Prim and Kruskal happen to return the same tree
here because the edge weights impose an unambiguous sequence of useful choices;
with ties, they may return different MSTs of equal weight.
Sorting costs O(E log E). In the implementation just described, the accepted
edges always form a forest and therefore number fewer than V. A DFS or BFS in
that forest costs O(V) in the worst case, and it may be performed for each of
the E candidates. The resulting bound is O(E log E + EV) time and
O(V+E) space, including the sorted edge list and forest. Quoting only
O(E log E) would not match this implementation because it would ignore the
explicit traversal-based cycle tests.
Common mistakes
Common mistake
(Taking the globally cheapest remaining edge in Prim)
Prim may choose only an edge crossing from its current tree to an outside vertex. A cheap edge joining two outside vertices does not extend the tree; an edge joining two inside vertices is stale and would create a cycle.
Common mistake
(Stopping when every vertex has appeared in some Kruskal edge)
Several disconnected components can collectively mention every vertex. Stop
only after |V|-1 edges have been accepted; for a connected input, the forest is
then one spanning tree.
Common mistake
(Confusing MST paths with shortest paths)
An MST minimizes one global sum. It does not promise that the path between a chosen pair, or from a chosen source, is cheapest in the original graph.
Common mistake
(Assuming nonnegative weights are required)
That restriction belongs to Dijkstra's greedy finalization argument, not to MST construction. Prim and Kruskal remain valid with negative edge weights; the cut/exchange reasoning compares weights but never assumes they are nonnegative.
Summary
- A spanning tree exists exactly when the undirected graph is connected, and
it contains all vertices with exactly
|V|-1edges. - An MST minimizes total selected-edge weight; it is not a shortest-path tree.
- Prim grows one connected tree by the lightest edge crossing its current cut.
- Kruskal grows a forest by considering edges in sorted order and rejecting an edge whenever its endpoints are already connected.
- The light crossing-edge exchange argument explains why both greedy choices are safe, while explicit cycle checks preserve the tree structure.
- Complexity must name the representation and data structures: the described
Prim edge queue costs
O(E log E), while sorted edges plus traversal-based Kruskal cycle checks costO(E log E + EV).
Exercises
Quick check
(Check 1. A connected graph has 9 vertices. How many edges must any spanning tree contain, and why can it contain no more?)
Use both parts of the word “tree”: connected and acyclic.
Quick check
(Check 2. In the five-vertex graph, Prim has already accepted AB, BD, and BC. Which queued edge is examined next, what happens to it, and which edge is then accepted?)
Inspect whether each endpoint is already in the current tree.
Quick check
(Check 3. Why is O(E log E) alone not a valid complete bound for the traversal-based Kruskal implementation in this note?)
Account for every HAS_PATH operation, not only sorting.
- In the five-vertex graph, remove edge
BD(2)and run Kruskal on the remaining six edges. Record every acceptance or rejection and the final total weight. - Prove the cycle criterion used by Kruskal: adding
(u,v)to a forest makes a cycle if and only if the forest already contains a path fromutov. - Construct a connected weighted graph with two different MSTs. Identify the weight tie that permits the two answers.
- Explain why Prim reports failure on a disconnected graph even if its priority queue implementation is otherwise correct.
Solutions
Solution · Solution to Check 1
It contains 9-1=8 edges. A connected graph on nine vertices needs at least
eight edges. A tree reaches that lower bound; adding any ninth edge to the tree
would connect two vertices that already have a unique tree path and therefore
create a cycle.
Solution · Solution to Check 2
The queue next exposes AD(5). Both A and D are already in the tree, so the
entry is stale and is rejected; otherwise it would create A-B-D-A. Prim then
accepts DE(6), bringing E into the tree and completing the four-edge MST.
Solution · Solution to Check 3
Sorting is O(E log E), but each candidate can trigger a DFS or BFS in a
forest containing V vertices and fewer than V edges. That search costs
O(V) and may occur E times, adding O(EV). The matched bound is therefore
O(E log E + EV).
Solution · Guided solutions to the longer exercises
- The new sorted order is
AB(1), BC(3), AD(5), DE(6), DC(7), CE(8). AcceptAB, acceptBC, acceptAD, and acceptDE; four accepted edges already span all five vertices. The total is1+3+5+6=15. No rejection is encountered before the stopping point. - If a path from
utovalready exists, that path plus(u,v)is a cycle. Conversely, if adding(u,v)creates a cycle, delete the new edge from that cycle; what remains is an old path fromutov. Thus the conditions are equivalent. - A triangle whose three edges all have weight
1works. Any two edges form a spanning tree of total2, so there are three MSTs. The equal weights mean the safe light edge need not be unique. - Prim reaches only the component containing its start vertex. Eventually the
queue becomes empty while fewer than
|V|-1edges have been accepted. No edge crosses from the reached component to the remaining vertices, which is exactly the certificate that no spanning tree of the whole graph exists.