Kruskal’s Greedy Algorithm

Context: FIT2004_MOC, FIT1058_MOC · merge many trees — sort every edge once, then absorb each one that joins two different components, tested by Union-Find (Disjoint Set) Parent Framework: Minimum Spanning Tree

Quick Revision

  • 🎯 Objective: sort ascending ➔ add each edge iff its endpoints lie in different trees ➔ stop at edges.
  • 📦 Core Components: sort ➔ the bottleneck | find(u)==find(v) ➔ the cycle test | union ➔ the acceptance | partial result is always a Forest.
  • ⚡ Key Constraint: the cycle test is the algorithm’s cost centre — a naive set copy makes it , union-find makes it free and hands the bill back to the sort.

📝 How It Works

1. Every Vertex Starts as Its Own Tree

  • Initial state ➔ singleton trees — a Forest with no edges.
  • An edge merges two trees ➔ adding fuses the tree containing with the tree containing .
  • Add only across trees ➔ if and are already in the same tree the edge closes a cycle ⟹ reject it.
  • Terminates as one tree ➔ components minus successful merges ; the scan may stop early the moment the count reaches one.
  • Contrast with Prim ➔ Prim grows a single tree and never needs the test; Kruskal maintains many and cannot avoid it.

2. The Three-Line Algorithm

  • Sort ➔ all edges by weight, ascending.
  • Scan ➔ for each edge in order: if find(u) != find(v): add it and union(u,v).
  • Stop ➔ when edges are accepted.
  • Which sort ➔ a comparison sort (Quick Sort / Merge Sort) at . Counting Sort and Radix Sort are not available — they need bounded integer keys, and edge weights are arbitrary (often real-valued), so the trade only pays when the problem states a small integer weight range.
  • collapses to ➔ ⟹ ; quote whichever the question uses, but know they are the same bound.

3. Implementation Ladder — Where the Cost Goes

  • Rung 1, Python set per component ➔ find is via a map array vertex ➔ set id, but union copies a set and rewrites the map ⟹ . Total .
  • Rung 2, copy the smaller set into the larger ➔ each element is moved only when its set doubles ⟹ amortised per union ⟹ .
  • Rung 3, Union-Find (Disjoint Set) with union-by-rank path compression ➔ both operations almost amortised ⟹ total , entirely the sort.
  • The reusable lesson (LO3) ➔ the algorithm never changed; only the ADT backing “same component?” did, and it moved the bound by a factor of ➔ the same lever as K-way Merge’s scan ➔ heap swap.

4. Correctness — the Cut Argument

  • Invariant ➔ the accepted edge set is always a subset of some MST.
  • Maintenance (exchange) ➔ let be the next accepted edge, joining components and . If the MST containing the current set omits , then has a cycle re-crossing the cut at some ; was the lightest remaining cross-edge so , and is a spanning tree of weight ⟹ also an MST, and it contains .
  • Termination ➔ the sorted list is finite and each iteration consumes one edge; connectedness guarantees acceptances.
  • Sealing ➔ at the end the accepted set is a spanning tree and a subset of an MST; equal edge counts force equality ⟹ it is an MST.
  • No non-negativity anywhere ➔ every step compares two weights ⟹ negative edges are legal ➔ Minimum Spanning Tree §3.

5. A Weaker Invariant That Looks Right (applied P10)

  • Candidate ➔ “each component of the current forest is an MST of its own vertices.”
  • True ➔ it follows from the real invariant: if a component were not minimal on its vertices, swapping in its own MST would lighten .
  • But useless ➔ it is strictly weaker and cannot close the proof. Counterexample: on the components and are each an MST of their vertices, yet no way of joining them yields the global MST.
  • The transfer (LO2) ➔ an invariant must be strong enough that invariant termination postcondition. A true statement the algorithm happens to maintain is not automatically a proof ➔ Invariant.

⚙️ Core Implementation

🔹 Kruskal with union-find

⚖️ Complexity

connected, edges supplied as a list.

CaseTimeAuxiliary spaceTrigger
Bestthe sort runs in full even if the first edges are all accepted
Averageunion-find cost is dominated
Worstevery edge scanned and tested; the sort still dominates
Edges pre-sorted, almost the only case where union-find is the cost centre
  • Naive rungs for comparison ➔ set-copy union · smaller-into-larger ➔ §3.
  • Space ➔ auxiliary for the parent array for the sorted edge list (input, if the edge list was given); the accepted tree is output.
  • Against Prim ➔ same ; the discriminator is density and input format, not the bound ➔ Minimum Spanning Tree Decision Matrix.

📊 Exam Execution Trace & Applied Exercises

Lecture graph, undirected: , , , , , , . Sorted: .

Manual Execution Trace

StepEdge find(u), find(v)Same tree?ActionForest after
0———init
noadd
noadd
noadd
yesreject (cycle)unchanged
noadd ➔ edges
———never reached (early exit)—

Final Extracted Output: MST , weight — the same optimum the lecture found by enumerating spanning trees, and reached in weight order, unlike Prim’s.

  • Read the trap ➔ step rejects although and were never joined directly; they became connected through two steps earlier. The test is on roots, never on the edge’s own history.

Applied Exercise — prep P2

Problem: same graph as Prim’s Algorithm’s prep exercise: , , , , , , , , , .

Final Extracted Output: , weight — the same edge set Prim returns from root , in a different order. Quote the order; the set alone does not show the algorithm ran.

⚠️ Common Mistakes

  • 💡 Sorting with Counting Sort or Radix Sort “because it is linear” ➔ only legal when the question bounds the weights to small integers. Unbounded or real weights force a comparison sort and the floor ➔ Sorting Problem.
  • 💡 Greedy optimal in general ➔ Kruskal is one of the rare cases where cheapest-first provably wins; do not transfer the habit to shortest paths or scheduling without a proof ➔ Greedy Algorithm.
  • 💡 Quoting the weak component-wise invariant as the proof ➔ true but too weak (§5); the marked invariant is subset of some MST.

🧠 Active Recall