Prim's algorithm
Grow the minimum spanning tree from one node: each time, add the lightest edge that joins a new node to the tree.
Prim's algorithm also builds a minimum spanning tree, but unlike Kruskal it grows it from one node. At every
moment there is one tree (in_tree). Of all the edges that join a node in the tree to a node outside it, take the
lightest and add the new node to the tree.
- Time:
O((V + E) log V) - Space:
O(V + E) - CLRS, section 21.2
- Horowitz–Sahni, chapter 6
- Grimaldi, chapter 13
The same nine-node graph as in the Kruskal example, this time grown from A; the tree weight is again 37.
from heapq import heappop, heappushINF = float("inf")def prim(graph, src):key = {v: INF for v in graph}parent = {v: None for v in graph}key[src] = 0pq = [(0, src)]in_tree = set()while pq:k, u = heappop(pq)if u in in_tree:continuein_tree.add(u)for v, w in graph[u]:if v not in in_tree:if w < key[v]:key[v] = wparent[v] = uheappush(pq, (w, v))return parent, key
Start
- Final
- Current node
(u) - Minimum spanning tree so far
- Edge being checked
Priority queue pq
empty
Local variables
Orange = changed in this step
graph dict
- 'A': [('B', 4), ('H', 8)]
- 'B': [('A', 4), ('H', 11), ('C', 8)]
- 'C': [('B', 8), ('I', 2), ('F', 4), ('D', 7)]
- 'D': [('C', 7), ('F', 14), ('E', 9)]
- 'E': [('D', 9), ('F', 10)]
- 'F': [('C', 4), ('D', 14), ('E', 10), ('G', 2)]
- 'G': [('F', 2), ('I', 6), ('H', 1)]
- 'H': [('A', 8), ('B', 11), ('G', 1), ('I', 7)]
- 'I': [('C', 2), ('G', 6), ('H', 7)]
Call stack
- prim()line 6
How the work grows with input size
Average of 5 random graphs per n (fixed seeds). Hollow marker = the input on screen.
- comparisons
- update
- current input (n=9)
Show as a table
| n | comparisons | update |
|---|---|---|
| 4 | 5.2 | 4.4 |
| 8 | 13.8 | 11 |
| 12 | 20.8 | 16 |
| 16 | 28 | 22.8 |
| 20 | 35.4 | 28.4 |
| 24 | 37.2 | 32.4 |
| 28 | 47.2 | 40 |
Explanation
The idea
Prim's algorithm also builds a minimum spanning tree, but unlike Kruskal it grows it from one node. At every
moment there is one tree (in_tree). Of all the edges that join a node in the tree to a node outside it, take the
lightest and add the new node to the tree.
key and the priority queue
For each node outside the tree, key[v] is the weight of the lightest edge known so far that joins v to the tree.
The node with the smallest key is found with a priority queue, exactly as in Dijkstra:
- The node with the smallest
keycomes out of the queue. If it is already in the tree, it is skipped. - It joins the tree.
- For each neighbour
voutside the tree: if edgeu — vis lighter thankey[v], updatekey[v]andparent[v]and pushvwith its new key.
How it differs from Dijkstra
The two algorithms have the same shape; only the test differs. Dijkstra asks dist[u] + w < dist[v] (the whole path
length from the source), Prim only w < key[v] (the weight of that one edge). That is why Dijkstra's tree gives
shortest paths and Prim's tree the smallest total weight.
Cost
With a binary-heap priority queue, O((V + E) log V).
What to watch in the trace
- The number next to each node is its
key; the blue edges (fromparent) are the tree so far. - On the "complete graph" input the priority queue fills with stale (struck-through) entries.
- Compare the final tree with Kruskal's result on the same graph ("textbook-style example"): both weigh 37.