Skip to main content

Section 9.22 Prim’s Spanning Tree Algorithm

For our last graph algorithm let’s consider a problem that online game designers and Internet radio providers face. The problem is that they want to efficiently transfer a piece of information to anyone and everyone who may be listening. This is important in gaming so that all the players know the very latest position of every other player. This is important for Internet radio so that all the listeners that are tuned in are getting all the data they need to reconstruct the song they are listening to. Figure 9.22.1 illustrates the broadcast problem.
The image illustrates a network graph representing the Broadcast Problem. It features seven nodes labeled A through G, with node A designated as the broadcast host. Directed edges connect the nodes, indicating the flow of the broadcast signal, with weights on the edges representing the cost or time of transmission. Four of the nodes are marked as listeners, depicted with computer icons. These listeners are at the terminus of the broadcast paths, showing the direction and flow of information from the host to the listeners through the network.
Figure 9.22.1. The Broadcast Problem.
There are some brute force solutions to this problem, so let’s look at them first to help understand the broadcast problem better. This will also help you appreciate the solution that we will propose when we are done. To begin, the broadcast host has some information that the listeners all need to receive. The simplest solution is for the broadcasting host to keep a list of all of the listeners and send individual messages to each. In Figure 9.22.1 we show a small network with a broadcaster and some listeners. Using this first approach, four copies of every message would be sent. Assuming that the least cost path is used, let’s see how many times each router would handle the same message.
All messages from the broadcaster go through router A, so A sees all four copies of every message. Router C sees only one copy of each message for its listener. However, routers B and D would see three copies of every message since routers B and D are on the cheapest path for listeners 1, 2, and 3. When you consider that the broadcast host must send hundreds of messages each second for a radio broadcast, that is a lot of extra traffic.
A brute force solution is for the broadcast host to send a single copy of the broadcast message and let the routers sort things out. In this case, the easiest solution is a strategy called uncontrolled flooding. The flooding strategy works as follows. Each message starts with a time to live (ttl) value set to some number greater than or equal to the number of edges between the broadcast host and its most distant listener. Each router gets a copy of the message and passes the message on to all of its neighboring routers. When the message is passed on the ttl is decreased. Each router continues to send copies of the message to all its neighbors until the ttl value reaches 0. It is easy to convince yourself that uncontrolled flooding generates many more unnecessary messages than our first strategy.
The solution to this problem lies in the construction of a minimum weight spanning tree. Formally we define the minimum spanning tree \(T\) for a graph \(G = (V,E)\) as follows. \(T\) is an acyclic subset of \(E\) that connects all the vertices in \(V\text{.}\) The sum of the weights of the edges in T is minimized.
Figure 9.22.2 shows a simplified version of the broadcast graph and highlights the edges that form a minimum spanning tree for the graph. Now to solve our broadcast problem, the broadcast host simply sends a single copy of the broadcast message into the network. Each router forwards the message to any neighbor that is part of the spanning tree, excluding the neighbor that just sent it the message. In this example A forwards the message to B. B forwards the message to D and C. D forwards the message to E, which forwards it to F, which forwards it to G. No router sees more than one copy of any message, and all the listeners that are interested see a copy of the message.
This image depicts a network graph titled "Minimum Spanning Tree for the Broadcast Graph". It consists of seven circular nodes labeled A through G, connected by lines which represent the edges of the tree. Each edge is marked with a number indicating the weight or cost associated with that connection. The structure is tree-like, with no cycles, starting from node A and branching out to all other nodes through the path of minimum total weight. Nodes B, C, E, F, and G are connected with the least number of edges to ensure coverage of the entire network, illustrating the concept of a minimum spanning tree in graph theory.
Figure 9.22.2. Minimum Spanning Tree for the Broadcast Graph.
The algorithm we will use to solve this problem is called Prim’s algorithm. Prim’s algorithm belongs to a family of algorithms called the “greedy algorithms” because at each step we will choose the cheapest next step. In this case the cheapest next step is to follow the edge with the lowest weight. Our last step is to develop Prim’s algorithm.
The basic idea in constructing a spanning tree is as follows:
While T is not yet a spanning tree
   Find an edge that is safe to add to the tree
   Add the new edge to T
The trick is in the step that directs us to “find an edge that is safe.” We define a safe edge as any edge that connects a vertex that is in the spanning tree to a vertex that is not in the spanning tree. This ensures that the tree will always remain a tree and therefore have no cycles.
The Python code to implement Prim’s algorithm is shown in Listing 2. Prim’s algorithm is similar to Dijkstra’s algorithm in that they both use a priority queue to select the next vertex to add to the growing graph.

Listing 2.

from pythonds.graphs import PriorityQueue, Graph, Vertex

def prim(G,start):
    pq = PriorityQueue()
    for v in G:
        v.setDistance(sys.maxsize)
        v.setPred(None)
    start.setDistance(0)
    pq.buildHeap([(v.getDistance(),v) for v in G])
    while not pq.isEmpty():
        currentVert = pq.delMin()
        for nextVert in currentVert.getConnections():
          newCost = currentVert.getWeight(nextVert)
          if nextVert in pq and newCost<nextVert.getDistance():
              nextVert.setPred(currentVert)
              nextVert.setDistance(newCost)
              pq.decreaseKey(nextVert,newCost)
The following sequence of figures (Figure 9.22.2 through Figure 9.22.2) shows the algorithm in operation on our sample tree. We begin with the starting vertex as A. The distances to all the other vertices are initialized to infinity. Looking at the neighbors of A we can update distances to two of the additional vertices B and C because the distances to B and C through A are less than infinite. This moves B and C to the front of the priority queue. Update the predecessor links for B and C by setting them to point to A. It is important to note that we have not formally added B or C to the spanning tree yet. A node is not considered to be part of the spanning tree until it is removed from the priority queue.
Since B has the smallest distance we look at B next. Examining B’s neighbors we see that D and E can be updated. Both D and E get new distance values and their predecessor links are updated. Moving on to the next node in the priority queue we find C. The only node C is adjacent to that is still in the priority queue is F, thus we can update the distance to F and adjust F’s position in the priority queue.
Now we examine the vertices adjacent to node D. We find that we can update E and reduce the distance to E from 6 to 4. When we do this we change the predecessor link on E to point back to D, thus preparing it to be grafted into the spanning tree but in a different location. The rest of the algorithm proceeds as you would expect, adding each new node to the tree.
This image illustrates the process of Prim’s algorithm applied to a graph. The graph has seven circular nodes labeled A through G, connected by lines with numbers indicating the weight of the edges. The algorithm starts at node A and progressively selects the edge with the lowest weight that connects a node inside the tree to a node outside the tree. As a result, the nodes are incrementally connected in a way that keeps the total weight of the tree at a minimum. The letters B, C, D, E, F, and G at the bottom signify the order in which the nodes are added to the growing tree.
Figure 9.22.3. Tracing Prim’s Algorithm.
The image continues to detail Prim’s algorithm, showing a partially completed minimum spanning tree. Nodes A, B, C, E, and F are connected with the shortest edges, marked with their respective weights and dashed lines indicating the edges considered at the current step. The remaining nodes D and G are yet to be connected. The bottom notation "PQ = C, D, E, F, G" lists the nodes in the priority queue awaiting to be connected.
Figure 9.22.4. Tracing Prim’s Algorithm.
The final image in the sequence demonstrates a further step in Prim’s algorithm. The minimum spanning tree now includes all nodes except for G, which is about to be connected. The edges between the nodes are highlighted, and the weights are updated to reflect the shortest paths from the tree to the remaining unconnected nodes. The bottom notation "PQ = D, E, F, G" updates the priority queue.
Figure 9.22.5. Tracing Prim’s Algorithm.
This image shows a further stage in Prim’s algorithm on a graph with seven nodes labeled A through G. The tree is almost complete, with solid lines connecting nodes A to F, and node G about to be connected. Each connection is annotated with the weight of the edge. The priority queue at the bottom, "PQ = E, F, G," lists the nodes considered for the next connection step.
Figure 9.22.6. Tracing Prim’s Algorithm.
The image depicts the penultimate step in Prim’s algorithm, where node G is the only one not yet included in the minimum spanning tree. The graph shows nodes A to F connected with the lowest weight edges, and the priority queue "PQ = F, G" indicates the next nodes to be considered for connection.
Figure 9.22.7. Tracing Prim’s Algorithm.
IThe final image represents the completion of Prim’s algorithm, where all nodes A through G are connected, forming the minimum spanning tree. Each edge’s weight is displayed, reflecting the algorithm’s choice of the shortest paths to connect all nodes without forming any cycles. The priority queue at the bottom, "PQ = G," shows the last node that was connected to complete the tree.
Figure 9.22.8. Tracing Prim’s Algorithm.
This image shows the final step of Prim’s algorithm where the minimum spanning tree is completed. All the nodes labeled from A to G are connected with the shortest paths indicated by the edge weights. The distances from the starting node are labeled next to each node with the letter ’d’ followed by the distance value. The priority queue (PQ) is empty, indicating that all nodes have been visited and the algorithm is complete.
Figure 9.22.9. Tracing Prim’s Algorithm.

Reading Questions Reading Question

1.

    Undirected graph with five nodes labeled A to E. Edges connect the nodes with the following weights: A-B with 1, A-D with 4, B-C with 2, C-D with 3, D-E with 6, and C-E with 5, forming a pentagon shape with a cross-connection between B and D.
    Figure 9.22.10. Tracing Prim’s Algorithm.
    Beginning at node E, how will Prim’s algorithm span across the graph?
  • {E, D, A, B, C}
  • Not quite, remember, this is a is a greedy algorithm, so it will try to choose the cheapest next step.
  • {E, C, B, A, D}
  • Correct!
  • {E, C, B, D, A}
  • Not quite, try again!
  • Both B and C
  • No, there is only one correct answer!
You have attempted of activities on this page.