Prim's and Kruskal's Algorithms
In computer science and graph theory, Prim's algorithm and Kruskal's algorithm are the two primary methods for finding a minimum spanning tree in a connected, undirected graph with weighted edges. A minimum spanning tree is a subset of the graph's edges that connects all vertices together without forming any cycles, while minimizing the total sum of the edge weights. These algorithms are foundational to network design, combinatorial optimization, and data clustering.
Both algorithms operate on the principle of greedy optimization, meaning they make the locally optimal choice at each step with the guarantee that these local choices will produce a globally optimal solution. This mathematical guarantee is rooted in matroid theory. A matroid is a mathematical structure that abstracts the notion of linear independence in vector spaces [1]. The forests of a graph form a specific type of matroid known as a graphic matroid. In any matroid, a greedy algorithm that selects elements in order of their weight is mathematically guaranteed to find an independent set of maximum or minimum weight. Because spanning trees are the maximal independent sets of a graphic matroid, greedy approaches like Prim's and Kruskal's will always succeed.
Despite sharing this underlying mathematical property, they approach the construction of the tree from entirely different perspectives. Kruskal's algorithm builds the tree edge by edge, sorting all available connections by weight and adding the lightest edges that do not create a closed loop. Prim's algorithm builds the tree vertex by vertex, starting from an arbitrary origin point and expanding outward by always adding the cheapest connection from the growing tree to a new, unvisited vertex.
The theoretical foundations of these algorithms date back to the early twentieth century, originating from practical engineering challenges such as the electrification of rural areas and the routing of early telecommunication lines. Today, they are standard subjects in computer science curricula and are implemented in countless software systems, ranging from network routing protocols to machine learning libraries.
Definition and the Minimum Spanning Tree Problem
To understand Prim's and Kruskal's algorithms, one must first define the mathematical structure they operate upon. A graph consists of a set of vertices (or nodes) and a set of edges connecting pairs of vertices. In the context of these algorithms, the graph is undirected, meaning the edges have no directional flow, and it is connected, meaning there is a path between any two vertices [2]. Each edge is assigned a numerical weight, which typically represents a cost, distance, or time associated with traversing that connection.
A spanning tree of such a graph is a subgraph that includes every vertex, contains no cycles, and is connected. Because it contains no cycles and connects all vertices, a spanning tree of a graph with $V$ vertices will always contain exactly $V - 1$ edges [3]. A single graph can have many different spanning trees. The Minimum Spanning Tree (MST) problem asks for the specific spanning tree where the sum of the edge weights is as small as possible.
If all edge weights in the graph are distinct, the minimum spanning tree is mathematically unique [4]. If multiple edges share the same weight, the graph may have multiple valid minimum spanning trees, though all such trees will have the exact same total weight. In practical software implementations, ties between equal weights can be broken arbitrarily or by assigning unique secondary identifiers to the edges. Furthermore, the exact same algorithms can be used to find a Maximum Spanning Tree by simply negating all the edge weights before processing.
The correctness of both Prim's and Kruskal's algorithms relies on two fundamental theorems in graph theory: the cut property and the cycle property. A cut is a partition of the graph's vertices into two disjoint sets. The cut property states that for any cut, the lightest edge that crosses from one set to the other must be part of the minimum spanning tree [2]. The cut property can be proven by contradiction. Assume the lightest edge crossing a cut is not in the MST. The MST must still connect the two sides of the cut, so it must use some other, heavier edge crossing the cut. If one adds the lightest edge to the MST, it creates a cycle with this heavier edge. By removing the heavier edge, one breaks the cycle, restores the tree structure, and reduces the total weight of the tree. This contradicts the assumption that the original tree was minimal.
The cycle property states that for any cycle in the graph, the heaviest edge in that cycle cannot be part of the minimum spanning tree [4]. If the heaviest edge in a cycle is part of the MST, removing it splits the tree into two components. Because it was part of a cycle, there is another edge in that cycle that connects the same two components. Adding this lighter edge reconnects the tree and reduces the total weight, again contradicting the assumption of minimality. Kruskal's algorithm explicitly relies on the cycle property by rejecting heavy edges that form loops, while Prim's algorithm explicitly relies on the cut property by always selecting the lightest edge crossing the boundary of the growing tree.
Mechanism of Kruskal's Algorithm
Kruskal's algorithm approaches the MST problem by treating the graph as a forest of isolated vertices that are gradually merged into a single tree. The mechanism is straightforward: it evaluates edges in ascending order of their weight and includes an edge in the final tree if and only if it connects two previously disconnected components [5].
The algorithm begins by sorting all edges in the graph from lightest to heaviest. It then initializes a forest where each vertex is its own separate tree. The algorithm iterates through the sorted list of edges. For each edge, it checks whether the two vertices it connects belong to the same tree. If they do, adding the edge would create a cycle, so the edge is discarded. If they belong to different trees, the edge is added to the MST, and the two trees are merged into one. This process continues until $V - 1$ edges have been added, at which point all vertices are connected in a single minimum spanning tree [6].
To efficiently track which vertices belong to which trees and to merge trees together, Kruskal's algorithm employs a disjoint set data structure, also known as a union-find data structure [7]. This structure supports two primary operations: Find, which determines the representative root of the set containing a given vertex, and Union, which merges two sets.
When implemented with two specific optimizations, the disjoint set operations become extremely fast. The first optimization is path compression, which flattens the structure of the tree during Find operations by making every visited node point directly to the root. The second optimization is union by rank, which ensures that the smaller tree is always attached to the root of the larger tree during Union operations, preventing the trees from becoming excessively deep.
The time complexity of Kruskal's algorithm is dominated by the initial sorting of the edges. Sorting $E$ edges takes $O(E \log E)$ time. The subsequent loop processes $E$ edges, performing two Find operations and potentially one Union operation per edge. In 1975, computer scientist Robert Tarjan published a rigorous mathematical analysis proving that with path compression and union by rank, the amortized cost of each disjoint set operation is bounded by $O(\alpha(V))$, where $\alpha$ is the inverse Ackermann function [8]. Because the inverse Ackermann function grows so slowly that it is less than 5 for any practical value of $V$ that could exist in the physical universe, the disjoint set operations take nearly linear time, $O(E \alpha(V))$. Thus, the overall time complexity remains $O(E \log E)$. Since $E$ can be at most $V^2$, $\log E$ is at most $2 \log V$, meaning the complexity can also be written as $O(E \log V)$ [2]. The space complexity is $O(V)$ to store the disjoint set data structure, plus the space required to store the graph and the sorted edges.
Mechanism of Prim's Algorithm
Unlike Kruskal's algorithm, which builds the tree from multiple disconnected fragments, Prim's algorithm builds the minimum spanning tree as a single, contiguous growing component. It starts by selecting an arbitrary vertex as the initial tree. It then repeatedly identifies the lightest edge that connects a vertex inside the tree to a vertex outside the tree, adds that edge to the tree, and brings the new vertex into the tree [9].
To implement this efficiently, Prim's algorithm uses a priority queue to keep track of the lightest edges connecting the growing tree to the unvisited vertices. Every vertex outside the tree is maintained in the priority queue, keyed by the minimum weight of an edge connecting it to any vertex already in the tree.
function Prim(Graph, start_vertex):
for each vertex v in Graph:
key[v] = infinity
parent[v] = null
key[start_vertex] = 0
PriorityQueue.insert_all(vertices, key)
while PriorityQueue is not empty:
u = PriorityQueue.extract_min()
for each neighbor v of u:
if v is in PriorityQueue and weight(u, v) < key[v]:
parent[v] = u
key[v] = weight(u, v)
PriorityQueue.decrease_key(v, key[v])
The algorithm begins by setting the key of the starting vertex to zero and all other vertices to infinity. The priority queue extracts the vertex with the minimum key (initially the starting vertex). The algorithm then examines all adjacent edges. If an edge leads to an unvisited vertex and its weight is less than that vertex's current key, the algorithm updates the vertex's key to the new, lower weight and records the current vertex as its parent. The priority queue is updated to reflect this decreased key. This process repeats until the priority queue is empty, at which point the parent pointers define the edges of the minimum spanning tree [10].
The time complexity of Prim's algorithm depends heavily on the implementation of the priority queue.[33] If a standard binary heap is used, extracting the minimum vertex takes $O(\log V)$ time, and decreasing a key takes $O(\log V)$ time. Since there are $V$ extractions and at most $E$ key decreases, the total time complexity is $O((V + E) \log V)$, which simplifies to $O(E \log V)$ for connected graphs [2].
The theoretical time complexity can be improved by using a Fibonacci heap. A Fibonacci heap is a collection of trees satisfying the minimum heap property. Unlike binary heaps, which maintain a strict structure, Fibonacci heaps are lazy. When a key is decreased, the node is simply cut from its parent and added to the root list, taking $O(1)$ time. The structural consolidation is deferred until the extract_min operation is called. This lazy evaluation allows a Fibonacci heap to support the decrease_key operation in $O(1)$ amortized time, while extract_min still takes $O(\log V)$ amortized time. This reduces the total time complexity to $O(E + V \log V)$ [11]. This improvement is highly advantageous for dense graphs where the number of edges $E$ is close to $V^2$.
However, Fibonacci heaps have high constant factors and significant pointer overhead, making them slow in practical software applications. As an alternative, many modern implementations of Prim's algorithm use Pairing Heaps. Pairing heaps are a self-adjusting data structure that offers excellent practical performance and simpler implementation compared to Fibonacci heaps [12]. While their exact theoretical bounds remain an open problem in computer science, their empirical performance in Prim's algorithm often surpasses that of both binary and Fibonacci heaps. The space complexity of Prim's algorithm is $O(V)$ to store the priority queue, keys, and parent pointers [2].
Discovery and History
The history of minimum spanning tree algorithms is characterized by multiple independent discoveries across different decades and countries, often driven by the specific technological needs of the era.[34]
The earliest known formulation and solution to the MST problem was published in 1926 by the Czech mathematician Otakar Borůvka [13]. Borůvka was approached by Jindřich Saxel, an employee of the West Moravian Power Company, who needed an economical method to design an electrical distribution network that would connect various cities in Moravia using the minimum total length of wire. Borůvka abstracted the cities as vertices and the distances as edge weights, inventing an algorithm that is now known as Borůvka's algorithm [14].
Four years later, in 1930, another Czech mathematician, Vojtěch Jarník, read Borůvka's paper and published a different solution to the same problem in the journal Práce Moravské Přírodovědecké Společnosti [15].[35] Jarník's method was the vertex growing approach that is today commonly known as Prim's algorithm. Because his work was published in Czech, it remained largely unknown to the broader international mathematical community for decades [16].
In 1956, the American mathematician Joseph Kruskal, working at Princeton University (and the Office of Naval Research), published his edge sorting algorithm in the Proceedings of the American Mathematical Society [6]. Kruskal was motivated by the Traveling Salesperson Problem, a famous optimization problem that asks for the shortest route visiting a set of cities and returning to the origin. Kruskal recognized that a minimum spanning tree provides a lower bound for the Traveling Salesperson Problem and can act as a starting point for approximation heuristics . In the same 1956 paper, Kruskal also described a variant of his algorithm that was essentially identical to Borůvka's 1926 method, unaware of Borůvka's prior publication.
In 1957, Robert C. Prim, also a researcher at Bell Labs, independently rediscovered Jarník's vertex growing algorithm. Prim was working on the design of shortest connection networks for telecommunications and published his findings in the Bell System Technical Journal [10]. Prim's clear exposition and the prominence of the journal led to the algorithm being widely associated with his name. In the same year, Loberman and Weinberger also independently published a similar algorithm for connecting terminals with minimum wire length [17].
Finally, in 1959, the Dutch computer scientist Edsger W. Dijkstra rediscovered the vertex growing algorithm yet again. Dijkstra published it alongside his famous shortest path algorithm in the journal Numerische Mathematik [18]. Dijkstra developed the shortest path algorithm to demonstrate the ARMAC (using a map of cities), a new computer built at the Mathematical Centre in Amsterdam, and he developed the MST algorithm to optimize the backplane wiring of the subsequent X1 computer [19]. Due to this complex history of independent discovery, Prim's algorithm is sometimes referred to in academic literature as Jarník's algorithm, the Prim-Jarník algorithm, or the DJP (Dijkstra-Jarník-Prim) algorithm [14].[36]
Important Variants
The foundational logic of Prim's and Kruskal's algorithms has been adapted into several important variants to suit different computational environments and theoretical constraints.
Borůvka's Algorithm: While historically preceding both Prim and Kruskal, Borůvka's algorithm is often taught as a variant. Instead of growing one tree (Prim) or sorting all edges (Kruskal), Borůvka's algorithm starts with a forest of single-vertex trees. In each phase, it simultaneously finds the minimum weight edge leaving each tree and adds all such edges to the forest. This reduces the number of trees by at least half in each phase, meaning the algorithm terminates in at most $O(\log V)$ phases. Because the minimum edge for each component can be found independently, Borůvka's algorithm is highly parallelizable and forms the basis for most distributed and parallel MST algorithms [20].
Reverse Delete Algorithm: This algorithm operates as the exact inverse of Kruskal's algorithm. Instead of starting with an empty graph and adding edges, it starts with the complete graph and sorts the edges in descending order of weight. It iterates through the sorted list, deleting each edge as long as its removal does not disconnect the graph. While conceptually simple, it is generally less efficient than Kruskal's algorithm because checking for graph connectivity after every deletion requires more computational overhead than the disjoint set operations used in Kruskal's [21].
Filter Kruskal: Developed by Osipov et al. in 2009, Filter Kruskal is a parallel variant designed for modern multi-core processors. It avoids the bottleneck of sorting all edges by partitioning the edges and using a filtering step to discard heavy edges that are guaranteed not to be in the MST. It then applies a parallel sort only to the remaining candidate edges, significantly improving performance on large, dense graphs [22].
Randomized and Optimal Algorithms: In 1995, Karger, Klein, and Tarjan introduced a randomized algorithm that finds the MST in $O(E + V)$ expected time. It alternates between steps of Borůvka's algorithm to reduce the number of vertices and random sampling to eliminate heavy edges [23]. In 2000, Bernard Chazelle introduced a deterministic algorithm that runs in $O(E \alpha(V))$ time. Chazelle achieved this by inventing the soft heap, a priority queue that intentionally corrupts a small fraction of its keys to achieve faster amortized bounds [24]. In 2002, Pettie and Ramachandran published an optimal deterministic minimum spanning tree algorithm. Its exact time complexity is unknown but is proven to be mathematically optimal, bounded by the decision tree complexity of the MST problem [25].
Real-World Applications
The ability to find a minimum spanning tree is a fundamental operation that supports a wide array of practical applications across computer science, engineering, and data analysis.
Network Design: The most direct application of MST algorithms is in physical network design. When laying cables for telecommunications, routing pipes for water supply, or designing electrical grids, engineers must connect a set of locations using the minimum amount of material. Prim's and Kruskal's algorithms provide the exact optimal topology for these networks, minimizing construction costs while ensuring all nodes are connected [10]. In logical network design, the Spanning Tree Protocol (STP) developed by Radia Perlman is used to prevent broadcast storms and routing loops in Ethernet networks [26]. While STP computes a spanning tree based on root bridge distances rather than a strict global minimum spanning tree, it relies on the same fundamental graph theory principles to ensure a loop-free topology [27].
Cluster Analysis: In data science, Kruskal's algorithm is mathematically equivalent to single linkage hierarchical clustering. If one treats data points as vertices and the Euclidean distance between them as edge weights, running Kruskal's algorithm builds a tree that connects all points. If the algorithm is stopped early, before the final $k - 1$ edges are added, the graph remains divided into $k$ disconnected components. These components represent $k$ distinct clusters of closely related data points. This technique is widely used in bioinformatics, epidemiology, and market research to group similar entities [28].
Image Segmentation: Computer vision systems use MST algorithms to partition digital images into distinct objects or regions. An image can be modeled as a grid graph where each pixel is a vertex, and edges connect adjacent pixels. The weight of an edge is calculated based on the difference in color, intensity, or texture between the two pixels. By applying a modified version of Kruskal's algorithm, the system can group similar pixels into segments, effectively identifying the boundaries of objects within the image. The Felzenszwalb-Huttenlocher algorithm is a prominent example of an MST based approach to image segmentation [29].
Approximation Algorithms for NP-Hard Problems: Minimum spanning trees are frequently used as subroutines to find approximate solutions to computationally intractable problems. For the Traveling Salesperson Problem, finding the absolute shortest route is NP-hard. However, the Christofides algorithm uses a minimum spanning tree as its foundational step [30]. The algorithm builds an MST, identifies all vertices with an odd degree, and computes a minimum weight perfect matching among those odd-degree vertices. By combining the MST edges with the matching edges, it creates an Eulerian graph. Tracing an Eulerian circuit through this graph and skipping previously visited vertices produces a valid tour. This method guarantees a tour that is at most 1.5 times the length of the optimal tour [31]. MSTs are similarly used to approximate solutions for the Steiner Tree problem, which involves connecting a subset of vertices in a graph using the minimum total weight [32].
Maze Generation: In video game design and recreational mathematics, randomized versions of Kruskal's or Prim's algorithms are used to generate perfect mazes. A grid of cells is treated as a graph, and random weights are assigned to the edges between adjacent cells. Computing the minimum spanning tree of this graph produces a maze where every cell is reachable, there are no closed loops, and there is exactly one unique path between any two cells [3].
Significance and Tradeoffs
The choice between Prim's and Kruskal's algorithms depends heavily on the specific characteristics of the graph being processed, particularly its density and the format of its data.
Graph density is the primary factor in determining which algorithm will perform faster in practice. A sparse graph has relatively few edges compared to its vertices, while a dense graph has many edges. Kruskal's algorithm has a time complexity of $O(E \log E)$, which is dictated by the need to sort all edges. For sparse graphs, the number of edges is small, making the sorting step very fast. Kruskal's algorithm uses simple data structures like arrays and disjoint sets. These structures have excellent cache locality and minimal memory overhead, meaning the CPU can process them with very few cache misses. This makes Kruskal's algorithm highly efficient for sparse networks [2].[35]
Conversely, Prim's algorithm with a standard binary heap runs in $O(E \log V)$ time. On sparse graphs, this is comparable to Kruskal's. However, on dense graphs, the number of edges becomes very large. Sorting $V^2$ edges for Kruskal's algorithm takes $O(V^2 \log V)$ time. Prim's algorithm can be optimized using a Fibonacci heap to achieve $O(E + V \log V)$ time. In a dense graph where the number of edges is proportional to $V^2$, this reduces to $O(V^2)$, which is strictly faster than Kruskal's $O(V^2 \log V)$ [11]. Therefore, Prim's algorithm is the mathematically superior choice for dense graphs.
Data representation also influences the choice. Kruskal's algorithm requires the graph to be represented as an edge list, which is easy to construct and sort. Prim's algorithm requires the graph to be represented as an adjacency list or adjacency matrix, allowing it to quickly find all neighbors of a specific vertex. If a graph is provided as a stream of edges, Kruskal's algorithm can process them more naturally. If the edges are already sorted, or if their weights are small integers that can be sorted in linear time using Radix Sort, Kruskal's algorithm avoids its $O(E \log E)$ bottleneck and runs in nearly linear $O(E \alpha(V))$ time [8].
In distributed computing environments, both Prim's and Kruskal's algorithms exhibit limitations. Prim's algorithm is inherently sequential; the tree must grow one vertex at a time, making it impossible to parallelize effectively. Kruskal's algorithm allows for parallel sorting of edges, but the disjoint set union operations are difficult to parallelize without conflicts. In these scenarios, Borůvka's algorithm is preferred, as its simultaneous edge selection allows different processors to work on different components of the graph independently [20].
Despite these tradeoffs, both algorithms remain essential tools in computer science. Their elegant reliance on the greedy choice property provides a rare example in combinatorial optimization where a locally greedy strategy is mathematically guaranteed to yield a globally optimal solution.
Sources (36)
- cs.cmu.edu
- mitpress.mit.edu
- algs4.cs.princeton.edu
- pearson.com
- Wikipedia:
- ams.org
- epubs.siam.org
- ACM Digital Library
- Wikipedia:
- IEEE
- ACM Digital Library
- web.stanford.edu
- dml.cz
- IEEE
- dml.cz
- mathshistory.st-andrews.ac.uk
- ACM Digital Library
- link.springer.com
- mathshistory.st-andrews.ac.uk
- Wikipedia:
- Internet Archive
- ACM Digital Library
- ACM Digital Library
- ACM Digital Library
- ACM Digital Library
- learning.acm.org
- ACM Digital Library
- jstor.org
- link.springer.com
- cs.cornell.edu
- apps.dtic.mil
- ics.uci.edu
- Frank Dehne, Jörg-Rüdiger Sack, Nicola Santoro, Algorithms and Data Structures (Berlin : Springer-Verlag, 1991)
- Steven S Skiena, The Algorithm Design Manual (Springer Science & Business Media, 2009)
- William Johnston, Alex McAllister, A Transition to Advanced Mathematics (Oxford University Press, 2009)
- Richard Bird, Jeremy Gibbons, Algorithm Design with Haskell (Cambridge University Press, 2020)