Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 28

Prim's Algorithm with Priority Queues

Recommended resources

Hello! Welcome back.

In our last lesson, we built a Minimum Spanning Tree (MST) using Kruskal's algorithm, which works by sorting all edges and progressively merging disconnected components into a single tree. Today, we'll explore a different, yet equally powerful, method for finding an MST: Prim's algorithm.

This lesson will guide you to: Implement Prim's algorithm for finding a Minimum Spanning Tree using a priority queue.

While Kruskal's algorithm builds a "forest" of trees that eventually connect, Prim's algorithm grows a single tree from an initial starting vertex. It continuously adds the cheapest possible edge that connects a vertex inside the growing tree to a vertex outside of it. This approach might remind you of Dijkstra's algorithm, but instead of minimizing the total path cost from a source, Prim's greedily minimizes the weight of the next single edge to be added to the tree.

The Greedy Strategy of Prim's Algorithm

Prim's algorithm operates on a simple and intuitive greedy principle based on a graph theory concept called the "cut property."

The Minimum Spanning Tree (MST) problem

To understand the theoretical foundation of Prim's algorithm, let's briefly look at the 'Cut Property'. This resource provides a concise explanation.

Read the section titled 'The Cut-Edge Observation'. This explains why greedily picking the minimum-weight edge that crosses a 'cut' (a partition of vertices) is a safe move when building an MST.

In essence, Prim's algorithm works by maintaining a "cut" that separates the vertices already in the MST from those that are not. At each step, it finds the minimum-weight edge crossing this cut and adds it to the tree.

The high-level process is as follows:

  1. Initialize an empty MST.
  2. Choose an arbitrary starting vertex and add it to a set of "visited" vertices.
  3. Add all edges connected to this starting vertex into a priority queue, which will store edges sorted by their weight.
  4. While the MST doesn't yet include all vertices in the graph:
    a. Extract the edge with the minimum weight from the priority queue.
    b. If the vertex at the other end of this edge has not been visited yet, add the edge to our MST.
    c. Mark the newly added vertex as "visited" and add all of its outgoing edges that lead to unvisited vertices into the priority queue.
    d. If the destination vertex was already visited, we simply discard the edge and repeat step 4a. This prevents cycles.

Visualizing Prim's Algorithm

Watching the algorithm in action is the best way to solidify your understanding. The following video provides an excellent step-by-step walkthrough, showing how the priority queue and visited set evolve.

G-45. Prim's Algorithm - Minimum Spanning Tree - C++ and Java

This video from 'take U forward' offers a detailed dry run of Prim's algorithm on a sample graph. Pay close attention to how the priority queue (min-heap) is used to always select the next best edge.

Watch from the initial configuration (01:40) through the full iteration (ending at 10:10). Observe how nodes are marked visited and how new edges are added to the priority queue. Notice what happens when an edge is pulled from the queue that points to an already visited node.

Prim's Algorithm Step-by-Step Illustration
This diagram provides a static, step-by-step visualization of Prim's algorithm. Starting from node D, the algorithm greedily adds the cheapest edge connecting the growing red tree to a gray, unvisited node (BD, then BE, then AB, then AC), until all nodes are part of the MST.

A key detail in the "lazy" version of Prim's algorithm (which is the most common implementation) is handling "stale" or "outdated" edges. A stale edge is one we pull from the priority queue that leads to a vertex we've already included in our MST via a different, cheaper path. Using a visited array allows us to efficiently detect and ignore these stale edges.


Implementing Prim's Algorithm

With a solid grasp of the logic, we can now turn to implementation. The core components are:

  • An adjacency list to represent the graph.
  • A min-priority queue to store potential edges, ordered by weight. In most languages, this will be a min-heap.
  • A visited array (or set) to track vertices already in the MST.

The following resource provides clean, well-structured code in multiple languages. As a software engineer, you should be able to translate the logic across these examples.

Prim's Algorithm for Minimum Spanning Tree (MST)

This GeeksForGeeks article provides direct implementations of Prim's algorithm using a priority queue and an adjacency list.

In the section 'Efficient Implementation using Priority Queue and Adjacency List', review the code for your preferred language (C++, Java, Python, and JavaScript are all available). Focus on the main loop: how an edge is extracted, how the visited check is performed to handle stale edges, and how adjacent edges are added to the queue.

The fundamental logic you'll see in the code follows this structure:

// Pseudocode for Prim's Algorithm
function prim(graph, startNode):
    pq = new PriorityQueue() // Stores {weight, destination_node}
    visited = new boolean[graph.numVertices]
    mst_total_weight = 0
    mst_edges = []

    // Start the algorithm
    pq.add({0, startNode}) // Add a dummy edge to kick things off

    while pq is not empty:
        {weight, u} = pq.extractMin()

        if visited[u]:
            continue // Ignore stale edge

        visited[u] = true
        mst_total_weight += weight
        // If you need the edge itself, you'd store the parent and add {parent, u} to mst_edges

        for each neighbor v of u with edge_weight w:
            if not visited[v]:
                pq.add({w, v})

    return mst_total_weight
Test your understanding!

Consider the graph from the image Execution of Prim's Algorithm. If we start at node A, what are the first three edges added to the MST, and in what order? What is the state of the priority queue right after node B is added to the MST?

Show answer
  1. First three edges:

    • A-B (weight 1)
    • A-C (weight 2)
    • B-D (weight 3)
  2. Priority Queue State after adding B:

    • Initial step (at A): The priority queue contains {1, B} and {2, C}.
    • First extraction: We extract {1, B}. Node B is added to the MST.
    • Update PQ: We add B's adjacent edges to the PQ. The new edges are {3, D} and {4, E}. The edge {2, C} from node A is still in the queue.
    • State: The priority queue now contains {2, C}, {3, D}, and {4, E}. The next edge to be extracted will be {2, C}.

Complexity Analysis

Understanding the performance of Prim's algorithm is crucial for deciding when to use it over Kruskal's. Let be the number of vertices and be the number of edges.

  • Time Complexity: or

    • The main while loop can run up to times in the worst case because we might add all edges to the priority queue.
    • Each operation on the priority queue (insertion and extraction) takes time, where is the number of items in the queue. In our case, the queue size can grow up to . So, the complexity of PQ operations is .
    • Since can be at most , we have . Therefore, is in .
    • Combining these, the total time complexity is dominated by the priority queue operations over all edges, giving us .
  • Space Complexity:

    • The adjacency list requires space.
    • The visited array requires space.
    • In the worst case (a dense graph), the priority queue might need to store an edge for every connection, leading to space.
    • Therefore, the total space complexity is .

The following video provides a concise summary of this analysis.

G-45. Prim's Algorithm - Minimum Spanning Tree - C++ and Java

To reinforce your understanding of the performance characteristics, watch this short segment on complexity.

Watch the explanation of time and space complexity starting from 14:56 until the end.


Conclusion

You have now implemented the two most famous algorithms for finding a Minimum Spanning Tree. This gives you a powerful set of tools for solving network optimization problems where the goal is total connectivity with minimum cost.

Key Takeaways:

  • Prim's algorithm finds an MST by greedily growing a single tree from a starting vertex.
  • It relies on the cut property: always add the minimum-weight edge that connects a visited vertex to an unvisited one.
  • An efficient implementation uses a priority queue to quickly find this minimum-weight edge and a visited array to prevent cycles and ignore stale edges.
  • The time complexity is , making it very efficient, especially for dense graphs where is close to .

Preview of the Next Lesson:
You've now implemented four major graph algorithms: Dijkstra's, Bellman-Ford, Kruskal's, and Prim's. In our next lesson, we will step back from implementation and move towards architectural thinking. We will compare and contrast these algorithms, discussing their specific use cases, constraints (e.g., negative weights, graph density), and performance trade-offs. This will equip you to make informed decisions about which algorithm to apply to a given design problem.