MIT 6.1220 — Lecture 8

Minimum Spanning Trees

Definition (Tree). A tree is a connected acyclic graph.

Definition (Spanning Tree). A spanning tree of a graph G=(V,E)G = (V, E) is a tree T=(V,F)T = (V, F) with FEF \subseteq E.

Claim. Every connected graph has a spanning tree.

Proof: There are many proofs (induction, BFS, …). Here's an algorithmic proof:

Algorithm. Initialize FEF \leftarrow E. While FF contains a cycle CC, repeatedly pick edges from CC at random to remove from FF. Return FF when done.

This terminates and yields a connected, acyclic graph (i.e. a spanning tree), done. Here's another proof:

Algorithm. Initialize FF \leftarrow \emptyset. While (V,F)(V, F) is not connected, repeatedly draw edges between connected components of (V,F)(V, F). Return FF when done.

This terminates and yields a connected, acyclic graph (i.e. a spanning tree), done.

Problem. (Minimum Spanning Tree) Find the spanning tree of a weighted graph with least total edge weight.

Here's a useful fact for building spanning tree intuition, from the cycle perspective.

Claim. Say T=(V,F)T = (V, F) is a spanning tree of G=(V,E)G = (V, E), and consider any edge e=uvEFe = \overline{uv} \in E \setminus F. Then:

Note that the first two bullet points don't really require eEe \in E, or care about GG at all, really.

And here's a similar fact, from the cutting perspective.

Claim. Say T=(V,F)T = (V, F) is a spanning tree of G=(V,E)G = (V, E), and consider any edge e=uvFe = \overline{uv} \in F. Then:

This inspires Kruskal's Algorithm, which constructs TT by adding edges to a forest.

Solution. (Kruskal's Algorithm) Here's the algorithm, in full.

  1. Initialize FF \leftarrow \emptyset. Initialize a Union-Find\textsc{Union-Find} data structure to store connected components. Sort EE by weight.

  2. Scan through all edges ei=(ui,vi)e_i = (u_i, v_i) for all i=1,2,,Ei = 1, 2, \dots, |E|, in increasing order of weight.

    • For each (ui,vi)(u_i, v_i) such that Find-Set(ui)Find-Set(vi)\textsc{Find-Set}(u_i) \neq \textsc{Find-Set}(v_i), add eie_i to FF and apply Union(ui,vi)\textsc{Union}(u_i, v_i).

  3. Finally, return FF.

Proof: It suffices to demonstrate by induction that FF is always a subset of some MST.

Suppose FTF \subseteq T initially, for some MST TT. Now we draw an edge e=(ui,vi)e = (u_i, v_i) between the connected component AA of uiu_i in (V,F)(V, F) and B:=VAB := V \setminus A. (Intuitively, we should think of edge ee as bridging across the “cut” V=ABV = A \sqcup B.)

If e=(ui,vi)Te = (u_i, v_i) \in T, we're fine. Otherwise, take the fundamental cycle of ee with respect to TT; this cycle must bridge across the “cut” V=ABV = A \sqcup B a second time along some edge ee'.

You can now check that T:=T{e}{e}T' := T \setminus \{e'\} \cup \{e\} is also an MST, so F{e}F \cup \{e\} remains a subset of an MST TT'.   \blacksquare

Here's our runtime analysis of Kruskal's Algorithm.

Solution. (Kruskal's Algorithm Runtime) There's only three major steps.

  1. Initializing the Union-Find\textsc{Union-Find} data structure takes O(V)O(|V|) time.

  2. Sorting EE takes O(ElogE)=O(ElogV)O(|E| \log |E|) = O(|E| \log |V|) time (since E=O(V2)|E| = O(|V|^2)).

  3. All of the Union-Find\textsc{Union-Find} operations take O(Eα(V))O(|E| \cdot \alpha(|V|)) time.

So in total, we can say the runtime of Kruskal's Algorithm is O(ElogV)O(|E| \log |V|).

This also inspires Prim's Algorithm, which grows FF by connecting vertices to a tree.

Remark. Recall the following properties of a priority queue implemented by a Fibonacci heap.

Solution. (Prim's Algorithm) Here's the algorithm, in full:

  1. Initialize F=(VF,EF)(,)F = (V_F, E_F) \leftarrow (\emptyset, \emptyset).

  2. Initialize a Priority Queue\textsc{Priority Queue} QQ and an array Parent[u]\textsc{Parent}[u].

    • The queue QQ will store keys vVv \in V with values d(v):=min{w(u,v)uVF and uvE}d(v) := \min \{ w(u, v) \mid u \in V_F \text{ and } \overline{uv} \in E\}.

    • The Parent\textsc{Parent} array will store for each vv the vertex with w(v,Parent[v])=d(v)w(v, \textsc{Parent}[v]) = d(v).

    • Initialize Parent[u]Null\textsc{Parent}[u] \leftarrow \textsc{Null} for all uVu \in V.

  3. Pick some starting vertex ss, and initialize Q.Insert(s,0)Q.\textsc{Insert}(s, 0) and Q.Insert(v,)Q.\textsc{Insert}(v, \infty) for all vsv \neq s.

  4. While QQ is nonempty…

    1. Add the edge (u,Parent[u])(u, \textsc{Parent}[u]) to FF, where uQ.DeleteMin()u \leftarrow Q.\textsc{DeleteMin}().

      (Add nothing when Parent[u]=Null\textsc{Parent}[u] = \textsc{Null}, as on the first iteration.)

    2. Now that we've added uu to FF, we need to make some updates. For each vv neighboring uu still in QQ

      • Update d(v)min{d(v),w(u,v)}d(v) \leftarrow \min\{d(v), w(u, v)\}, and update Q.DecreaseKey(v,d(v))Q.\textsc{DecreaseKey}(v, d(v)) as needed.

      • If d(v)d(v) was updated, then also update Parent[v]u\textsc{Parent}[v] \leftarrow u.

  5. Return FF when done.

Proof: Again, we wish to show FF is invariably a subset of some MST.

It's actually the same argument as before. Consider the cut V=VF(VVF)V = V_F \sqcup (V \setminus V_F). You can do whatever to bridge this cut and the result will still be an ST, minimal if you do the “whatever” with minimal weight.   \blacksquare

This runs in O(E+VlogV)O(|E| + |V|\log |V|) time, since…