For the complete documentation index, see llms.txt. This page is also available as Markdown.

21. Minimum Spanning Trees

Exercises

21.1-1

Available in the latest revision of the IM.

21.1-2

Available in the latest revision of the IM.

21.1-3

Available in the latest revision of the IM.

21.1-4

Available in the latest revision of the IM.

21.1-5

Available in the latest revision of the IM.

21.1-6

Available in the latest revision of the IM.

21.1-7

Available in the latest revision of the IM.

21.1-8

Let G=(V,E)G = (V, E) be a connected, undirected graph with nn vertices. Let TT and TT' be two minimum spanning trees (MSTs) of GG. For any real number ww, let EwE_{\le w} be the set of all edges in GG that have a weight less than or equal to ww. Let Gw=(V,Ew)G_{\le w} = (V, E_{\le w}) be the subgraph of GG containing all vertices of GG but only the edges in EwE_{\le w}. Let c(w)c(w) be the number of connected components in the subgraph GwG_{\le w}.

Consider the edges of TT that have a weight of ww or less. Let's call this set TwT_{\le w}. Because TT is an MST, TwT_{\le w} must form a maximal cycle-free subgraph (a spanning forest) of GwG_{\le w}.

Why is it maximal? If it were not maximal, there would exist some edge eEwe \in E_{\le w} that we could add to TwT_{\le w} without creating a cycle. Since TT is a spanning tree of the entire graph GG, adding ee to TT must create exactly one cycle, CC. As in the proof of Theorem 21.1 and Exercise 21.1-6,, we can conclude that every other edge in CC must have a weight w(e)w\le w(e) \le w (the well-known cycle property of MSTs). This means the entire cycle CC is contained within GwG_{\le w}, which contradicts the assumption that adding ee to TwT_{\le w} wouldn't create a cycle.

Any spanning forest of a graph with nn vertices and cc connected components has exactly ncn-c edges. Thus, the number of edges in both TT and TT' with weight w\le w is exactly nc(w)n - c(w). Because the number of components c(w)c(w) depends only on the original graph GG and the threshold ww (and not on the specific spanning tree chosen), both TT and TT' will always have the exact same number of edges with weight w\le w. Since this holds true for any arbitrary weight ww, the distribution of edge weights must be identical in both trees. Consequently, their sorted lists of edge weights, LL, must be the same.

21.1-9

Available in the latest revision of the IM.

21.1-10

Available in the latest revision of the IM.

★ 21.1-11 🌟

The most efficient way to solve this is by using the cycle property of minimum spanning trees (see Exercise 21.1-8). When you add any new edge to an existing spanning tree, it creates exactly one simple cycle. The cycle property states that the heaviest edge on any cycle cannot belong to the minimum spanning tree. Let the edge whose weight decreased be e=(u,v)e = (u, v). The step-by-step algorithm is as follows:

  1. Add the edge: Temporarily add the updated edge ee to your existing minimum spanning tree TT. The graph T{e}T \cup \{{e}\} now contains exactly one cycle, CC.

  2. Find the cycle: Run a DFS on the tree TT starting from vertex uu to find the path to vertex vv. This path, along with edge ee, forms the cycle CC.

  3. Find the heaviest edge: Traverse the edges of the cycle CC and identify the edge emaxe_{max} that has the maximum weight. Update TT:

    1. If w(e)<w(emax)w(e) < w(e_{max}), then replace emaxe_{max} with ee in TT.

    2. Otherwise, keep the original tree TT.

Because TT is a tree with V\vert{}V\vert{} vertices, it has exactly V1\vert{}V\vert{} - 1 edges. Running a DFS to find a path in a tree takes O(V)O(V) time. Finding the maximum weight edge on that path also takes O(V)O(V) time. Therefore, the entire algorithm runs in O(V)O(V) time, which is much faster than recalculating the MST from scratch.

21.2-1

Available in the latest revision of the IM.

The solution in the IM builds upon the property proven in Exercise 21.1-8.

21.2-2

Available in the latest revision of the IM.

21.2-3

Available in the latest revision of the IM.

21.2-4

Available in the latest revision of the IM.

21.2-5

Available in the latest revision of the IM.

21.2-6

Available in the latest revision of the IM.

★ 21.2-7

Available in the latest revision of the IM.

★ 21.2-8

Available in the latest revision of the IM.

Problems

21-1 Second-best minimum spanning tree

Available in the latest revision of the IM.

21-2 Minimum spanning tree in sparse graphs

a.

By Exercise 21.1-1, we know that the edges added to TT are safe. Furthermore, the correctness of the Prim's algorithm ensures that AA is an MST of GG'. The edges in TT and Aorig={(x,y).orig′:(x,y)A}A_\text{orig}=\{(x, y).\text{orig′} : (x, y) ∈ A\} are disjoint by construction.

We claim that their union TT' is an MST of GG. Suppose there exists some other spanning tree TT'' for GG such that w(T)<w(T)w(T'')<w(T'). Because the edges in TT are safe, we can assume TT'' contains all the edges in TT. If we take TT'' and contract all the edges belonging to TT (just like the algorithm does to create GG'), the remaining edges of TT'' must form a spanning tree for GG'. Let's call this contracted remainder AA'. The total weight of the hypothetical better tree is w(T)=w(T)+w(A)w(T'') = w(T) + w(A'). We assumed this is less than w(T)=w(T)+w(A)w(T') = w(T) + w(A). Subtracting w(T)w(T) from both sides leaves us with w(A)<w(A)w(A')<w(A). But this is impossible!

Notice that TT' is a tree, since vertices of GG' represent sets of connected vertices of GG via TT and AA connects these hyper-vertices.

b.

This follows from the way how MST-Reduce works. Each unmarked vertex uu of GG is placed into the same vertex set where vv belongs for a selected minimum-weight edge (u,v)(u,v). Therefore, every hyper-vertex of GG' represents a vertex set of size at least 2. Furthermore, all such sets of vertices are disjoint.

c.

Here is the O(E)O(E) implementation that augments the base variant:

1

Component Labeling (Replacing Disjoint Sets)

Instead of maintaining a disjoint-set data structure, we can select all the safe edges first and then use a simple graph traversal to identify the hyper-vertices.

  1. Select Edges: Iterate through the original vertices VV. For each unmarked vertex uu, scan its adjacency list to find the minimum-weight incident edge (u,v)(u,v). Add this edge to a list TT and mark both uu and vv. Since we only scan the adjacency list of each vertex once to find its minimum edge, this entire process takes O(E)O(E) time.

  2. Find Components (O(V)O(V) time): Create a temporary graph using the original vertices VV and only the edges in TT. Run a standard BFS or DFS on this graph to find its connected components.

  3. Assign IDs (O(V)O(V) time): During the traversal, assign a unique integer ID (from 1 to kk, where kV/2k \le \vert{}V\vert{}/2 by part (b)) to each connected component. Store this in an array comp[x] = ID for every original vertex xx. This array perfectly replaces the FIND-SET(x) operation and provides O(1)O(1) lookups.

2

Resolving Parallel Edges

Now we need to map the original edges to the new hyper-vertices and filter out duplicate (parallel) edges, keeping only the lightest ones.

  1. Map Edges (O(E)O(E) time): Iterate through all original edges (x,y)G.E(x, y) \in G.E. Look up their new endpoints: u = comp[x] and v = comp[y]. If uvu \neq v, we have a valid edge for GG'. Store it in a flat array as a tuple: (min(u,v),max(u,v),(x,y))( \min(u, v), \max(u, v), (x,y)). Notice that we also store the original edge.

  2. Radix Sort (O(E)O(E) time): We need to group edges that connect the same pair of hyper-vertices. Because the hyper-vertex IDs are integers bounded by V\vert{}V\vert{}, we can sort the list of tuples using Radix Sort (specifically, two passes of Counting Sort: first on the max(u,v)\max(u, v) coordinate, then on the min(u,v)\min(u, v) coordinate). Radix Sort takes O(E+V)O(E + V) time, which simplifies to O(E)O(E) since EV1|E| \ge |V| - 1 in a connected graph.

  3. Filter Duplicates (O(E)O(E) time): Because the list is now sorted lexicographically by endpoint pairs, any duplicate edges between the same two hyper-vertices will be adjacent in the array. Do a single linear scan through the sorted array. For each block of identical endpoint pairs, select the one with the minimum weight, add it to G.EG'.E, and discard the rest.

3

Finalizing GG'

Now that G.EG'.E contains the exact, deduplicated set of edges for the contracted graph, simply iterate over G.EG'.E one last time to populate the adjacency lists for GG'.

d.

Observe that G.VG.V/2|G'.V| \le |G.V|/2 by part (b) and G.EG.E|G'.E| \le |G.E|. Each phase demands O(E)O(E) time by part (c), which gives O(kE)O(kE) total time.

e.

By part (b), each phase reduces the number of vertices by at least half. After kk phases, the contracted graph GG' has VV2k\vert{}V'\vert{} \le \frac{V}{2^k} vertices. The number of edges remains bounded by EE. Combining these, the total asymptotic running time is

T(k)=O(kE+E+V2klgV)=O(kE+V2klgV).T(k) = O\left(kE + E + \frac{V}{2^k} \lg V\right) = O\left(kE + \frac{V}{2^k} \lg V\right).

If we set k=lglgVk = \lg \lg V, then T(k)=O(ElglgV+V)=O(ElglgV).T(k)=O(E \lg \lg V + V)=O(E \lg \lg V). Because we are working with a single connected component, we know EV1E \ge V - 1, which justifies the last step.

This hybrid algorithm is specifically designed for sparse graphs (where EVE \approx V), which is where standard Prim's algorithm gets bottlenecked by its O(VlgV)O(V \lg V) term. Our running time function T(k)T(k) represents a classic trade-off:

  • As kk increases, the kEkE term grows, but the V2klgV\frac{V}{2^k} \lg V term shrinks.

  • As kk decreases, the kEkE term shrinks, but the V2klgV\frac{V}{2^k} \lg V term grows.

To find the minimum asymptotic running time, we must balance these two competing terms so that neither dominates and balloons the overall complexity.

  • What if kk is smaller? If we pick a smaller kk, say k=lglgVω(1)k = \lg \lg V - \omega(1) (meaning kk is asymptotically smaller), the second term becomes O(V2ω(1))O(V \cdot 2^{\omega(1)}), which easily overtakes ElglgVE \lg \lg V for sparse graphs, worsening the time complexity.

  • What if kk is larger? If we pick a larger kk, say k=lglgV+ω(1)k = \lg \lg V + \omega(1), the first term becomes O(ElglgV+Eω(1))O(E \lg \lg V + E \cdot \omega(1)), which exceeds our O(ElglgV)O(E \lg \lg V) bound.

Therefore, setting k=lglgVk = \lg \lg V is the precise "sweet spot" where the cost of contracting vertices exactly neutralizes the expensive priority-queue operations of Prim's algorithm, yielding the absolute lowest upper bound.

f.

Let's set up the inequality ElglgV<E+VlgVE \lg \lg V < E + V \lg V. Notice that the ElglgVE \lg \lg V term on the left will always be asymptotically larger than the standalone EE term on the right. Therefore, for the left side to win, the VlgVV \lg V term must be the dominant force on the right side, and it must be larger than ElglgVE \lg \lg V. Thus,

ElglgV<VlgV    E=o(VlgVlglgV).E \lg \lg V < V \lg V \implies \vert{}E\vert{} = o\left(\frac{\vert{}V\vert{} \lg \vert{}V\vert{}}{\lg \lg \vert{}V\vert{}}\right).

21-3 Alternative minimum-spanning-tree algorithms

Available in the latest revision of the IM.

21-4 Bottleneck spanning tree

a.

Suppose TT is an MST, whose heaviest edge is emaxe_{\max}, but not a bottleneck spanning tree. This means there exists some other spanning tree, let's call it TBT_B (the actual bottleneck tree), whose maximum edge weight is strictly less than w(emax)w(e_{\max}). If we remove emaxe_{\max} from TT, it splits it into two disconnected components, creating a cut (V1,V2)(V_1, V_2). Because TBT_B is a valid spanning tree of the whole graph GG, it must connect V1V_1 and V2V_2. Therefore, TBT_B must contain at least one edge ee that crosses this exact cut. Since eTBe \in T_B, its weight can be no larger than the maximum edge in TBT_B. From our initial assumption, we know the maximum edge of TBT_B is strictly less than w(emax)w(e_{\max}). Now we can safely construct T=(T{emax}){e}T' = (T - \{e_{\max}\}) \cup \{e\}. Because ee crosses the cut created by removing emaxe_{\max}, it perfectly reconnects the two components. TT' is a valid spanning tree withw(T)=w(T)w(emax)+w(e)<w(T)w(T') = w(T) - w(e_{\max}) + w(e) < w(T). This contradicts the fact that TT is a minimum spanning tree.

Thus, a minimum spanning tree is a bottleneck spanning tree.

b.

If a bottleneck spanning tree has a value of at most bb, it means there is some way to connect all the vertices in the graph using only edges that have a weight of bb or less. If we simply throw away all edges heavier than bb, we just need to check if the remaining graph is still connected. This can be done by counting the number of vertices visited via BFS or DFS. We can solve this task in O(V+E)O(V + E) linear time.

c.

Let G=(V,E)G=(V,E) be a connected, undirected graph as input to our algorithm. A graph is only connected if VE+1\vert{}V\vert{} \le \vert{}E\vert{} + 1, so E=Ω(V)|E|=\Omega(V).

1

Find the Median Weight

Using a linear-time selection algorithm, find the median weight mm of all the edges currently in EE.

2

Partition the Edges

Divide the edges into three sets based on this median. For example, E<mE_{<m} are edges lighter than mm.

3

Test

Using part (b) as a subroutine, check if the graph is connected using only the edges in E<mE=mE_{<m} \cup E_{=m}. There are two possible outcomes:

  • Test is negative: This means it is impossible to span the graph using edges of weight mmor less. The bottleneck edge must be strictly greater than mm. Because we have to use heavier edges to connect the graph, all the edges in E<mE=mE_{<m} \cup E_{=m} are "safe" to use without exceeding our eventual bottleneck. Just like in MST-Reduce, find the connected components formed by these edges and contract each component into a single hyper-vertex. Recurse on this newly contracted graph using only the edges in E>mE_{>m}.

  • Test is positive: This means the bottleneck is m\le m. We can safely throw away all edges in E>mE_{>m}. But we still need to know if the bottleneck is exactly mm, or if it's strictly less than mm. Run the test again, but this time only use the edges in E<mE_{<m}.

    • If positive: The bottleneck is strictly less than mm. Throw away E=mE_{=m} as well, and recurse on the original vertices using only E<mE_{<m}.

    • If negative: The bottleneck is exactly mm. Stop and return the corresponding DFS tree.

The time complexity forms the recurrence relation T(E)=T(E/2)+O(E)T(E) = T(E/2) + O(E), which evaluates to a strictly linear O(E)O(E) total time.

Last updated