1. Overview

In graph theory, there are two main algorithms for calculating the minimum spanning tree (MST):

  1. Kruskal’s algorithm
  2. Prim’s algorithm

In this tutorial, we’ll explain both and have a look at differences between them.

2. Minimum Spanning Tree

Spanning-tree is a set of edges forming a tree and connecting all nodes in a graph. The minimum spanning tree is the spanning tree with the lowest cost (sum of edge weights).

Also, it’s worth noting that since it’s a tree, MST is a term used when talking about undirected connected graphs.

Let’s consider an example:

MST

As we can see, red edges form the minimum spanning tree. The total cost of the MST is the sum of the weights of the taken edges. In the given example, the cost of the presented MST is 2 + 5 + 3 + 2 + 3 + 3 = 18.

However, this isn’t the only MST that can be formed. For example, instead of taking the edge between E and G, we can take the edge between D and G, and the cost will stay the same. From that, we can notice that different MSTs are the reason for swapping different edges with the same weight.

Therefore, the different order in which the algorithm examines edges with the same cost results in different MSTs. However, of course, all of these MSTs will surely have the same cost.

3. Kruskal’s Algorithm

3.1. Main Idea

The main idea behind the Kruskal algorithm is to sort the edges based on their weight. After that, we start taking edges one by one based on the lower weight. In case we take an edge, and it results in forming a cycle, then this edge isn’t included in the MST. Otherwise, the edge is included in the MST.

The problem is with detecting cycles fast enough. In order to do this, we can use a disjoint set data structure. The disjoint set data structure allows us to easily merge two nodes into a single component. Also, it allows us to quickly check if two nodes were merged before.

Therefore, before adding an edge, we first check if both ends of the edge have been merged before. If so, we don’t include the edge in the MST. Otherwise, we add the edge to the MST and merge both nodes together inside the disjoint set data structure.

3.2. Algorithm

In this algorithm, we’ll use a data structure named dsu which is the disjoint set data structure we discussed in section 3.1. Take a look at the pseudocode for Kruskal’s algorithm.

algorithm KruskalAlgorithm:
    // INPUT
    //     edges = the list of all edges in the input graph
    // OUTPUT
    //     Returns the cost and the edges of the minimum spanning tree (MST)

    sort(edges)
    totalCost <- 0
    mst <- []

    for edge in edges:
        // If the current edge does not form a cycle
        if not dsu.isMerged(edge.u, edge.v):
            totalCost <- totalCost + edge.weight
            mst.add(edge)
            dsu.merge(edge.u, edge.v)

    return totalCost, mst

Firstly, we sort the list of edges in ascending order based on their weight. Secondly, we iterate over all the edges. For each edge, we check if its ends were merged before. If so, we just ignore this edge.

Otherwise, we increase the total cost of the MST and add this edge to the resulting MST. Also, we merge both ends of this edge inside the disjoint set data structure.

In the end, we just return the total cost of the calculated MST and the taken edges.

The complexity of the Kruskal algorithm is O(E \cdot log(V)) , where E is the number of edges and V is the number of vertices inside the graph. The reason for this complexity is due to the sorting cost.

3.3. Analysis

Since the complexity is O(E \cdot log(V)), the Kruskal algorithm is better used with sparse graphs, where we don’t have lots of edges.

However, since we are examining all edges one by one sorted on ascending order based on their weight, this allows us great control over the resulting MST. Since different MSTs come from different edges with the same cost, in the Kruskal algorithm, all these edges are located one after another when sorted.

Therefore, when two or more edges have the same weight, we have total freedom on how to order them. The order we use affects the resulting MST. Of course, the cost will always be the same regardless of the order of edges with the same weight. However, the edges we add to mst might be different.

Another aspect to consider is that the Kruskal algorithm is fairly easy to implement. The only restrictions are having a good disjoint set data structure and a good sort function.

4. Prim’s Algorithm

4.1. Main Idea

Basically, Prim’s algorithm is a modified version of Dijkstra’s algorithm. First, we choose a node to start from and add all its neighbors to a priority queue.

After that, we perform multiple steps. In each step, we extract the node that we were able to reach using the edge with the lowest weight. Therefore, the priority queue must contain the node and the weight of the edge that got us to reach this node. Also, it must sort the nodes inside it based on the passed weight.

For each extracted node, we add it to the resulting MST and update the total cost of the MST. Also, we add all its neighbors to the queue as well.

In order to obtain a better complexity, we can ensure that each node is presented only once inside the queue. For example, we can use a function addOrUpdate that takes the node with the weight and the edge that led us to this node.

In case the node was already inside the queue, and the new weight is better than the stored one, the function removes the old node and adds the new one instead. Otherwise, if the node isn’t inside the queue, it simply adds it along with the given weight.

4.2. Algorithm

Consider the following pseudocode for Prim’s algorithm.

algorithm PrimsAlgorithm:
    // INPUT
    //     G = the given graph
    //     source - the node to start from
    // OUTPUT
    //    Returns the cost of the MST

    totalCost <- 0
    included <- an empty set
    Q.addOrUpdate(source, 0, null)

    while Q is not empty:
        u = Q.getNodeWithLowestWeight()
        totalCost += u.weight

        if u.edge != null:
            mst.add(u.edge)

        included.add(u.node)

        for v in G.neighbors(u.node):
            if v.node not in included:
                Q.addOrUpdate(v.node, weight(u.node, v.node), v.edge)
    
    return totalCost, mst

In the beginning, we add the source node to the queue with a zero weight and without an edge. We use null to indicate that we store an empty value here. Also, we initialize the total cost with zero and mark all nodes as not yet included inside the MST.

After that, we perform multiple steps. In each step, we extract the node with the lowest weight from the queue. For each extracted node, we increase the cost of the MST by the weight of the extracted edge. Also, in case the edge of the extracted node exists, we add it to the resulting MST.

When we finish handling the extracted node, we iterate over its neighbors. In case the neighbor is not yet included in the resulting MST, we use the addOrUpdate function to add this neighbor to the queue. Also, we add the weight of the edge and the edge itself.

The complexity of Prim’s algorithm is O(E + V \cdot log(V)) , where E is the number of edges and V is the number of vertices inside the graph.

4.3. Analysis

The advantage of Prim’s algorithm is its complexity, which is better than Kruskal’s algorithm. Therefore, Prim’s algorithm is helpful when dealing with dense graphs that have lots of edges.

However, Prim’s algorithm doesn’t allow us much control over the chosen edges when multiple edges with the same weight occur. The reason is that only the edges discovered so far are stored inside the queue, rather than all the edges like in Kruskal’s algorithm.

Also, unlike Kruskal’s algorithm, Prim’s algorithm is a little harder to implement.

5. Comparison

Let’s highlight some key differences between the two algorithms.

Kruskal

Prim

Multiple MSTs

Offers a good control
over the resulting MST

Controlling the MST
might be a little harder

Implementation

Easier to implement

Harder to implement

Requirements

Disjoint set

Priority queue

Time Complexity

O(E \cdot log(V))

O(E + V \cdot log(V))

As we can see, the Kruskal algorithm is better to use regarding the easier implementation and the best control over the resulting MST. However, Prim’s algorithm offers better complexity.

6. Conclusion

In this tutorial, we explained the main two algorithms for calculating the minimum spanning tree of a graph. Firstly, we explained the term MST. Secondly, we presented Kruskal’s and Prim’s algorithms and provided analysis for each one.

Thirdly, we summarized by providing a comparison between both algorithms.