Prim's Algorithm

Imagine an internet service provider laying fiber-optic cable to connect a hundred cities. Digging trenches is expensive. Each pair of cities has a different cost to connect based on distance and terrain. The provider wants every city linked into one network at the lowest total digging cost. The goal isn't the fastest connection between any two specific cities. It's the cheapest way to bind them all together. This is the minimum spanning tree (MST) problem. Prim's algorithm is one of its two classic solutions. Prim's algorithm builds the MST the way a colony expands. Start from a single city. Then repeatedly annex whichever unconnected neighbor is cheapest to reach from the territory already claimed. This "grow one tree from a seed" strategy contrasts with Kruskal's Algorithm. Kruskal's instead sorts all edges globally and merges disjoint fragments. Prim's approach turns out to be structurally identical to Dijkstra's Algorithm. They share the same priority-queue skeleton and the same relaxation loop. The difference is that Prim's optimizes a different quantity. This makes it an excellent bridge between shortest-path and spanning-tree thinking. The algorithm matters because many problems reduce to finding a minimum spanning tree. These include network design, clustering, image segmentation, and approximation algorithms for harder problems. One example is the traveling salesman problem. When a graph is dense — many edges relative to vertices — Prim's algorithm becomes especially useful. Implemented with the right data structures, it is asymptotically the fastest known approach. Its incremental, tree-growing nature also makes it a natural fit for streaming and incremental-update settings. This article gives a precise statement of the algorithm. It includes a fully worked pseudocode implementation using a priority queue. It also includes a diagram of the frontier as it grows. A rigorous correctness proof is built on the Cut Property. Finally, it offers an honest complexity analysis comparing the binary-heap and Fibonacci-heap variants.

Key takeaways
  • Prim's algorithm grows a single tree from an arbitrary start vertex, always adding the cheapest edge crossing from the tree to the rest of the graph.
  • It is structurally the same loop as Dijkstra's Algorithm: a priority queue keyed by a "best edge so far" value, relaxed as new vertices are visited.
  • Correctness follows directly from the Cut Property: at every step, the chosen edge is the unique minimum-weight edge crossing some cut, so it must belong to some MST.
  • Running time is O(E log V) with a binary heap, and O(E + V log V) with a Fibonacci heap — the latter matching the theoretical optimum for dense graphs.
  • Unlike Kruskal's algorithm, Prim's needs no union-find structure and maintains only one connected component throughout.
  • It performs best on dense graphs and in adjacency-matrix form; Kruskal's tends to win on sparse graphs.
The core insight

Prim's algorithm builds one minimum spanning tree by always crossing its current boundary through the cheapest available edge, a choice the cut property guarantees is always safe.

The idea

A spanning tree of a connected, undirected graph G=(V,E)G = (V, E) is a subset of ∣V∣−1|V| - 1 edges that connects all vertices without forming a cycle. A minimum spanning tree is a spanning tree whose total edge weight is smallest among all spanning trees. Prim's algorithm computes an MST by maintaining a single growing tree TT, initially just one arbitrary vertex, and repeatedly adding the lightest edge that connects a vertex already in TT to a vertex outside TT. The key structural object is the cut: a partition of VV into two nonempty sets SS and V∖SV \setminus S. Any edge with one endpoint in each side is said to cross the cut. The cut property states that if an edge is the unique minimum-weight edge crossing some cut, then it belongs to every MST of the graph (or at least one, if ties are broken consistently). Prim's algorithm is essentially a mechanized proof of the cut property: at every iteration, the current tree TT defines the cut (T,V∖T)(T, V \setminus T), and the algorithm greedily selects the minimum-weight edge across that exact cut. To find that minimum edge efficiently at every step, the algorithm maintains, for every vertex vv not yet in the tree, a running value `key[v]` equal to the weight of the cheapest edge seen so far connecting vv to the tree, along with a pointer `parent[v]` recording which tree vertex offers that edge. A min-priority queue keyed on `key[v]` lets the algorithm repeatedly extract the frontier vertex with the smallest key in logarithmic time. This is precisely the same bookkeeping used in Dijkstra's Algorithm — the difference is that Dijkstra's key is the cumulative distance from the source, while Prim's key is the weight of a single connecting edge, ignoring everything already traveled.

How it works

The algorithm proceeds in three phases: initialization, repeated extraction of the cheapest frontier vertex, and relaxation of its neighbors. 1. Initialize. Pick an arbitrary start vertex rr. Set `key[r] = 0` and `key[v] = ∞` for every other vertex. Set `parent[v] = NIL` for all vv. Insert all vertices into a min-priority queue keyed by `key`. 2. Extract. Repeatedly remove the vertex uu with the smallest `key` value from the queue. This vertex is now considered "in the tree." If `parent[u] ≠ NIL`, the edge `(parent[u], u)` is added to the MST. 3. Relax. For every neighbor vv of uu still in the queue, if the weight w(u,v)w(u, v) is smaller than the current `key[v]`, update `key[v] = w(u, v)` and `parent[v] = u`, and adjust vv's position in the priority queue (a `decrease-key` operation). The process terminates when the queue is empty, at which point every vertex has been pulled in exactly once and the `parent` pointers describe a spanning tree of minimum total weight. ```text PRIM-MST(G, w, r): for each vertex v in G.V: key[v] ← ∞ parent[v] ← NIL in_tree[v] ← false key[r] ← 0 Q ← min-priority queue containing all vertices, keyed by key[] while Q is not empty: u ← EXTRACT-MIN(Q) # vertex with smallest key not yet in tree in_tree[u] ← true if parent[u] ≠ NIL: add edge (parent[u], u) to MST result for each (u, v) in G.adj[u]: if in_tree[v] = false and w(u, v) < key[v]: key[v] ← w(u, v) parent[v] ← u DECREASE-KEY(Q, v, key[v]) return set of edges {(parent[v], v) : v ≠ r} ``` Two points deserve emphasis. First, `key[v]` never represents a path length or accumulated cost — it is always the weight of a single edge, refreshed whenever a cheaper direct connection to the tree is discovered. Second, the algorithm never needs to check for cycles explicitly (unlike Kruskal's, which needs union-find): because `EXTRACT-MIN` only ever pulls a vertex not already in the tree, every added edge necessarily connects a new vertex, so a cycle can never form.

Figure
The frontier grows one vertex at a time, always crossing the cheapest available edge
tree T (in priority queue: extracted) A B C 2 4 D E F 3 ← min crossing edge 9 7 Next step: EXTRACT-MIN picks D via edge (B,D) weight 3 — cheapest edge crossing the cut (T, V\T)

Complexity & correctness

Correctness. The proof is a direct instantiation of the cut property, proved by induction on the number of edges added. Invariant: After each iteration, the edges chosen so far form a subset of some minimum spanning tree of GG. Base case: With zero edges chosen, the empty set is trivially a subset of any MST. Inductive step: Suppose the invariant holds after kk edges have been added, forming tree TT on vertex set SS. Consider the cut (S,V∖S)(S, V \setminus S). The algorithm's next chosen edge e=(u,v)e = (u, v), with u∈Su \in S and v∉Sv \notin S, is by construction the minimum-weight edge crossing this cut — this is exactly what `key[]` and `EXTRACT-MIN` guarantee, since every vertex outside SS has its `key` value set to the weight of the lightest edge connecting it to SS, and the extracted vertex has the smallest such key among all frontier vertices. By the cut property, this minimum-weight crossing edge belongs to some MST consistent with the edges already chosen. Hence the invariant is preserved. Termination: The loop runs exactly ∣V∣|V| times (once per `EXTRACT-MIN`), adding ∣V∣−1|V| - 1 edges in total (the first extraction adds no edge, since `parent[r] = NIL`). When the queue empties, S=VS = V, and by the invariant the collected edges form a spanning tree that is a subset of an MST on all of VV — but any spanning tree with ∣V∣−1|V|-1 edges that is a subset of an MST must equal that MST, since both have the same edge count. Hence the output is a genuine minimum spanning tree. Note that this argument silently assumes GG is connected; if GG is disconnected, Prim's algorithm as stated only discovers a minimum spanning tree of the connected component containing rr, and some `key` values remain ∞\infty forever.

Time complexity. The dominant costs are `EXTRACT-MIN` (called ∣V∣|V| times) and `DECREASE-KEY` (called at most once per edge, so O(E)O(E) times).

  • Binary heap: `EXTRACT-MIN` costs O(log⁡V)O(\log V), `DECREASE-KEY` costs O(log⁡V)O(\log V). Total:
O(Vlog⁡V+Elog⁡V)=O(Elog⁡V)O(V \log V + E \log V) = O(E \log V)

since the graph is connected, so E≥V−1E \ge V - 1.

  • Fibonacci heap: `EXTRACT-MIN` costs O(log⁡V)O(\log V) amortized, but `DECREASE-KEY` costs O(1)O(1) amortized. Total:
O(Vlog⁡V+E)O(V \log V + E)

This is the asymptotically fastest known comparison-based bound for general graphs and matches Dijkstra's complexity with the same data structure.

  • Adjacency matrix without a heap: scanning for the minimum key directly among O(V)O(V) candidates each round costs O(V2)O(V^2) overall, which beats O(Elog⁡V)O(E \log V) when the graph is dense (EE close to V2V^2), since log⁡V\log V overhead is avoided entirely.

Space complexity is O(V+E)O(V + E): the adjacency list (or matrix), the `key`/`parent`/`in_tree` arrays of size VV, and the priority queue holding up to VV entries.

Tradeoffs. Prim's algorithm requires no global edge sort and no union-find, but every `DECREASE-KEY` touches the priority queue, so a naive implementation without a proper decrease-key-capable heap (e.g., a plain binary heap with lazy deletion instead) degrades performance. On sparse graphs, Kruskal's Algorithm with a good union-find structure (O(Elog⁡E)O(E \log E), dominated by the initial sort) is often simpler to reason about and just as fast in practice. Prim's real advantage emerges on dense graphs and in settings — such as incremental network design — where growing a single connected structure is more natural than merging fragments.

Worked example

Consider six routers A,B,C,D,E,FA, B, C, D, E, F with the following candidate cable costs (undirected):

EdgeWeight
A–B4
A–C1
B–C2
B–D5
C–D8
C–E10
D–E2
D–F6
E–F3

Start Prim's algorithm at vertex AA.

StepExtracted vertexEdge addedWeightkey[] updates after this step
1A——B: 4 (via A), C: 1 (via A)
2C (key=1, smallest)A–C1B: 2 (via C, improved from 4), D: 8 (via C), E: 10 (via C)
3B (key=2, smallest)B–C2D: 5 (via B, improved from 8)
4D (key=5, smallest)B–D5E: 2 (via D, improved from 10), F: 6 (via D)
5E (key=2, smallest)D–E2F: 3 (via E, improved from 6)
6F (key=3, smallest)E–F3queue empty

Resulting MST edges: A–C (1), B–C (2), B–D (5), D–E (2), E–F (3). Total weight = 13. Trace check: at Step 2 the algorithm rejects the tempting-looking edge A–B (weight 4) in favor of A–C (weight 1) — the priority queue never even considers A–B until B's key is set, and by the time B is extracted, its key has already been lowered from 4 to 2 via C, correctly reflecting that reaching B through C is cheaper than a direct A–B cable. This illustrates why `key[]` must be continuously relaxed rather than fixed at initialization: the cheapest known connection to the tree can improve as the tree grows, exactly mirroring the relaxation step in Dijkstra's algorithm, but comparing single edges rather than path sums.

Common pitfalls & traps

PitfallWhy it happensFix
Confusing `key[v]` with shortest-path distanceBeginners copy Dijkstra's logic wholesale`key[v]` is the weight of one edge to the tree, never a cumulative sum
Forgetting to skip `parent[r] = NIL` when outputting edgesThe start vertex has no incoming tree edgeGuard the "add edge" step with a NIL check
Using a plain array scan and calling it O(log⁡V)O(\log V)Assuming a heap is present when it isn'tMatch your stated complexity to the actual data structure implemented
Running on a disconnected graph and expecting a full spanning treeThe cut property only applies within one componentCheck connectivity first, or run Prim's per component
Re-inserting an already-extracted vertex into the queue during relaxationSloppy `in_tree` bookkeepingAlways test `in_tree[v] = false` before relaxing
Assuming Prim's finds the unique MSTTies in edge weight allow multiple valid MSTs of equal total weightState the result is an MST, not the MST, when weights repeat

Pioneers & history

Prim's algorithm is named for Robert C. Prim, who published it in 1957 while working at Bell Labs, though the same greedy tree-growing idea had been discovered independently and earlier by the Czech mathematician Vojtěch Jarník in 1930, making the algorithm's true origins a striking case of parallel discovery separated by nearly three decades. Edsger W. Dijkstra rediscovered and popularized essentially the same algorithm in 1959, in the same paper that introduced his shortest-path algorithm — a historical fact that explains why the two algorithms share an almost identical priority-queue skeleton.

Origins · 1 minRobert Clay Prim and how this was discovered
RC
Robert Clay Prim
1921–2018 · American mathematician and computer scientist at Bell Labs who formalized the minimum spanning tree growth algorithm in 1957, building on earlier network-optimization work motivated by telephone infrastructure design

History
1930
Vojtěch Jarník describes the tree-growing minimum spanning tree method in a Czech mathematics journal, unnoticed outside Central Europe for decades.
1956
Joseph Kruskal publishes the competing edge-sorting approach to the MST problem.
1957
Robert Prim publishes the algorithm at Bell Labs, independently of Jarník, motivated by telecommunication network design.
1959
Edsger Dijkstra rediscovers the same method and publishes it alongside his shortest-path algorithm, cementing the structural link between the two.
1984
Michael Fredman and Robert Tarjan's Fibonacci heap gives Prim's algorithm its asymptotically optimal O(E+Vlog⁡V)O(E + V \log V) running time.

Where this matters

Prim's algorithm underlies network design tools that plan physical infrastructure — fiber-optic backbones, electrical grids, and water distribution networks — where the goal is connecting every site at minimum total cost. In computer vision, MST-based image segmentation algorithms use Prim's-style growth to cluster pixels into regions by treating pixel similarity as edge weight. Clustering algorithms in bioinformatics use minimum spanning trees, often built with Prim's algorithm on dense similarity graphs, to infer evolutionary or structural relationships between genetic sequences. Approximation algorithms for the metric traveling salesman problem construct a 2-approximate tour by first computing an MST — commonly via Prim's algorithm when the underlying graph is dense or given as a distance matrix — and then traversing it. Network routing protocols and circuit design tools for VLSI layout also invoke MST computation as a subroutine, frequently choosing Prim's algorithm specifically because chip layouts tend to produce dense, matrix-representable graphs where its O(V2)O(V^2) variant is fastest in practice.

:::quiz[What does key[v] represent during execution of Prim's algorithm?]

  • The shortest path distance from the root to v
  • The total number of edges explored so far
  • The weight of the lightest edge connecting v to the current tree ✓
  • The degree of vertex v in the final MST

Ask the tutor: How would you modify Prim's algorithm to efficiently support edge weight updates without recomputing the MST from scratch?

Is Prim's algorithm faster than Kruskal's algorithm?

It depends on graph density and implementation. With a binary heap, Prim's runs in O(Elog⁡V)O(E \log V), essentially the same as Kruskal's O(Elog⁡E)O(E \log E) (since log⁡E=O(log⁡V)\log E = O(\log V) for simple graphs). On dense graphs, Prim's with a plain adjacency matrix achieves O(V2)O(V^2), beating Kruskal's sort-dominated bound. With a Fibonacci heap, Prim's reaches O(E+Vlog⁡V)O(E + V \log V), the best known bound for general graphs. Kruskal's tends to be simpler to implement correctly on sparse graphs, since it avoids decrease-key bookkeeping entirely. In practice, benchmark both on your specific graph shape before choosing.

Can Prim's algorithm handle negative edge weights?

Yes, unlike some shortest-path algorithms, Prim's algorithm works correctly with negative edge weights, because it never reasons about cumulative path sums — only about the single cheapest edge crossing the current cut at each step. The cut property makes no assumption about weight positivity; it only requires a strict, well-defined minimum among crossing edges. This is a key structural difference from Dijkstra's algorithm, which fails with negative weights precisely because it accumulates path costs that a later negative edge could invalidate.

Does Prim's algorithm produce a unique minimum spanning tree?

Not necessarily. If the graph has distinct edge weights, the MST is provably unique, and Prim's algorithm will find it regardless of starting vertex. If edge weights tie, multiple spanning trees can share the same minimum total weight, and different tie-breaking rules in `EXTRACT-MIN` or different start vertices can yield different — but equally minimal — trees. The algorithm always produces a correct MST, just not always the same one across runs when ties exist.

Why does starting vertex choice not affect the total MST weight?

Because the cut property is a statement about edges and cuts, not about any particular starting point. No matter which vertex begins the tree, every step still selects the minimum-weight edge crossing the current cut, and the induction proof of correctness never references which vertex was chosen first. The specific sequence of vertices visited may differ, and with tied weights the exact tree may differ, but the total weight of any MST produced is always the graph's true minimum.

How does Prim's algorithm compare structurally to Dijkstra's algorithm?

They share an almost identical skeleton: both maintain a priority queue of vertices keyed by a numeric value, both repeatedly extract the minimum, and both relax neighboring edges upon extraction. The difference is entirely in what the key represents. Dijkstra's `dist[v]` is the shortest cumulative path weight from the source, requiring `dist[u] + w(u,v)` in relaxation. Prim's `key[v]` is the weight of a single edge to the tree, requiring just `w(u,v)` in relaxation, with no accumulation. This distinction is exactly why Dijkstra's fails on negative edges while Prim's does not.

What happens if two edges crossing a cut have equal minimum weight?

The cut property still guarantees that at least one MST contains one of the tied minimum edges, but not that both must be included. Prim's algorithm will deterministically pick whichever tied edge its priority queue happens to extract first (often decided by insertion order or an internal tie-breaking rule), and the resulting tree is still a valid MST. Different valid MSTs may result from different tie-breaking choices, but all share the same total weight, since they are all genuinely minimal.

Is Prim's algorithm used for directed graphs?

No. Minimum spanning trees are defined only for undirected, connected graphs, since the concept relies on edges being usable in either direction to reach every vertex. The directed analogue — finding a minimum-weight arborescence rooted at a specific vertex — is a different problem solved by the Chu–Liu/Edmonds' algorithm, which does not reduce to a simple priority-queue frontier growth like Prim's does.

Before this