Abstract
What it is: Prim’s Algorithm is a Greedy Algorithm that builds a Minimum Spanning Tree (MST) for a connected, undirected, weighted graph by repeatedly growing a single tree one cheapest edge at a time.
- Category: Minimum Spanning Tree / Greedy Algorithm
- Input: Undirected, connected graph with (not necessarily positive) edge weights
- Output: A list of edges forming a minimum spanning tree of (total weight is minimized among all spanning trees)
- Paradigm: Greedy, priority-queue-driven frontier expansion
- Typical use cases: network design (minimizing total cable/pipe/wire length), clustering, approximation algorithms that use an MST as a subroutine
Core Logic (High-Level)
- Put all vertices in (undiscovered).
- Pick any vertex to start from.
- Put in (the tree built so far).
- Repeat until all vertices are in :
- Find the minimum edge that has one vertex in and one vertex outside it
- Move that outside endpoint from into .
- Add that edge to the output.
Naively, step 4.1 means scanning every edge crossing the boundary of each iteration. The cost array below avoids that: it caches the cheapest crossing edge per vertex, so step 4.1 becomes “pick the frontier vertex with the smallest cost” instead.
Key Idea
Instead of keeping track by looking at edges, put the cost information in the vertices themselves (
costarray) —cost(u)is the cheapest edge weight seen so far connecting to the tree.Update a vertex by putting the lowest cost vertex from into , then re-check its neighbors: if a neighbor now has a cheaper bridge through this new tree vertex, update its
cost(same “relax” idea as Dijkstra’s Algorithm, just comparing one edge weight instead of a cumulative path).
Pseudocode (Mid-Level Implementation)
Similar to Dijkstra’s Algorithm, Prim’s uses the value instead of — same loop, same priority queue, but the relaxation compares just the single edge weight against cost(u), not a cumulative path length.
Algorithm 39 Prim's Algorithm
Input: undirected connecred graph with positive edge weights
Output: : a list of edges that describe a minimum spanning tree
procedure Prim's()
Pick a random vertex
Initialize = empty,
Initialize for all
Initialize
Initialize
Initialize empty
while is not empty do
Pick the that has the value
for each neighbor of do
if then
Move to
Set
Set
Move from to
Move into
return
Variables & Data Structures
| Name | Type | Purpose |
|---|---|---|
X | Set | Vertices already included in the growing spanning tree |
F | Set / Priority Queue | Frontier — vertices discovered (adjacent to X) but not yet added, keyed by cost |
U | Set (implicit) | Vertices not yet discovered at all () |
cost | Array (vertex → number) | Cheapest known edge weight connecting the vertex to the current tree ; starts at except |
prev | Array (vertex → vertex) | The tree-neighbor that offered the current best cost; used to reconstruct the actual MST edges |
output | List of edges | Accumulates the edges that make up the final MST |
v, u | Vertex | Current minimum-cost vertex being finalized / candidate neighbor |
Helper Functions / Operations Used
ℓ(v, u)— the weight of the edge betweenvandu; O(1) lookup with an adjacency list/matrix- Pick with lowest
cost(v)— adeleteminoperation on the priority queue backingF - Relax a neighbor — if , update
cost(u)andprev(u), and move/re-prioritizeuinF(adecreasekey, or an insert ifuwas previously inU)
Low-Level Implementation
The low-level implementation is essentially Dijkstra’s low-level implementation with
distrenamed tocostand the relaxation condition changed from to — i.e. compare against the single edge weight, not the cumulative path weight. Samemakequeue/deletemin/decreasekeyprimitives apply, and the same array-vs-binary-heap trade-off discussion carries over directly.
Proof of Correctness
Claim: Upon termination, the edges in output form a minimum spanning tree of .
Loop Invariant (Cut Property): At the start of each iteration of the while loop, the edges already added to output form a subset of some minimum spanning tree of — equivalently, can always be extended to a full MST using only edges not yet ruled out.
- Initialization: Before the first iteration,
outputis empty and . The empty edge set is trivially a subset of any MST, so the invariant holds. - Maintenance: Suppose the invariant holds — ‘s edges so far are consistent with some MST . Consider the cut . By the Cut Property, the minimum-weight edge crossing this cut is guaranteed to be in some MST. Prim’s always selects exactly this edge next — the vertex with lowest
cost(v)is, by construction, the endpoint of the cheapest edge crossing the cut (sincecost(v)was set to the weight of the cheapest edge from to during relaxation). So adding tooutputkeeps the invariant true — either already contains this edge, or swapping it into (removing whatever edge used to connect ) produces another MST of equal or lower weight, since the swapped-in edge is minimum-weight across the cut. See Cut Property for the full proof of why this swap argument works. - Termination: Each iteration moves exactly one vertex from (or , via relaxation) into , so after iterations all vertices are in and
outputcontains exactly edges — the size of a spanning tree on a connected graph. The while loop then ends because becomes empty.
Why it doesn’t miss/duplicate vertices: Each vertex is moved into exactly once (guarded implicitly by only picking , and only ever contains vertices not already in ); since is connected, every vertex is eventually discovered as a neighbor of some vertex already in , so no vertex is permanently stuck in .
Conclusion: By induction, when the loop terminates (), output is a spanning tree consistent with an MST at every step, and since it spans all of with exactly minimum-cut-respecting edges, output is a minimum spanning tree.
Time & Space Complexity Analysis
Basically has the same runtime as Dijkstra’s, using the same formula — the one difference is that Prim’s requires a connected input graph, so . That’s why the term drops out of the totals below: it’s always dominated by , so the runtime is stated purely in terms of and together rather than as a sum of two competing terms.
- Binary Heap —
- Array —
General Case
| Complexity | Notes | |
|---|---|---|
| Time | Every vertex is finalized once (deletemin), every edge is examined at most twice (once from each endpoint) as a relaxation candidate | |
| Space | cost, prev arrays + priority queue holding the frontier, plus output of size $ |
Implementation-Dependent Variations
| Data Structure Choice | Impact on Time | Impact on Space | Notes |
|---|---|---|---|
| Array as Priority Queue | total — deletemin is , decreasekey is | Better for dense graphs where | |
| Binary Heap as Priority Queue | total — deletemin and decreasekey are both | + supplemental “address book” array to locate vertices in the heap | Better for sparse graphs where ; note $ |
| Adjacency list vs matrix | vs for scanning neighbors | vs | Matrix only worth it on already-dense graphs |
Best / Worst / Average Case
- Best case: Still — Prim’s has no early-exit condition; it must process every vertex and consider every edge to guarantee the minimum spanning tree, regardless of graph shape.
- Worst case: Same order — dense graph maximizes both the number of
decreasekeycalls and, if using an array-backed PQ, the cost of eachdeletemin. - Average case: Same asymptotic order; Prim’s has no probabilistic behavior to average over.
Drawbacks / Constraints
- Preconditions: must be connected and undirected — Prim’s does not handle directed graphs (there is no directed-graph analogue of a spanning tree in the same sense) and will not produce a spanning structure if is disconnected (some vertices would remain in forever, stuck at ).
- Unlike Dijkstra’s, negative edge weights are fine. Prim’s only ever compares single-edge weights (), never cumulative path weights, so the greedy cut-property argument still holds even with negative weights — there’s no analogue of Dijkstra’s “a later negative edge could undercut an already-finalized path” failure mode.
- Not suitable for: Finding shortest paths between vertices — an MST minimizes total tree weight, not pairwise path weight; use Dijkstra’s Algorithm (non-negative weights) or Bellman-Ford (negative weights allowed) for shortest paths instead.
- MST is not unique in general. If multiple edges tie for minimum weight across a cut, different tie-breaking choices can produce different (but equally minimal-weight) spanning trees.
- Alternatives to consider: Kruskal’s Algorithm — a different greedy MST algorithm that sorts all edges globally and uses union-find, often preferable for very sparse graphs or when edges are already sorted/streamed.