0% found this document useful (0 votes)
21 views34 pages

CH23 Weighted Graphs

Uploaded by

Isan
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
21 views34 pages

CH23 Weighted Graphs

Uploaded by

Isan
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 34

***This chapter is a bonus Web chapter

CHAPTER 23
Weighted Graphs and Applications

Objectives

 To represent weighted edges using adjacency matrices and priority


queues (§23.2).
 To model weighted graphs using the WeightedGraph class that
extends the AbstractGraph class (§23.3).
 To design and implement the algorithm for finding a minimum
spanning tree (§23.4).
 To define the MST class that extends the Tree class (§23.4).
 To design and implement the algorithm for finding single-source
shortest paths (§23.5).
 To define the ShortestPathTree class that extends the Tree class
(§23.5).
 To solve the weighted nine tail problem using the shortest-path
algorithm (§23.6).

213
© Copyright 2012 by Pearson Education, Inc. All Rights Reserved.
23.1 Introduction

<key point>
A graph is a weighted graph if each edge is assigned with a
weight. Weighted graphs have many practical applications.
<end key point>

<Side Remark: problem>


Figure 22.1 assumes that the graph represents the flights among cities.
You can apply the BFS to find the minimal number of flights between two
cities. Assume that the edges represent the driving distances among the
cities as shown in Figure 23.1. How do you find the minimal total
distances for connecting all cities? How do you find the shortest path
between two cities? This chapter will address these questions. The
former is known as the minimal spanning tree problem and latter the
shortest path problem.

Seattle

2097 Boston
983
Chicago
1331 214
807
1003 New York
787
Denver
533
1267 1260
599
888
San Francisco 1015 Kansas City

381 1663
864

496
Los Angeles 1435 Atlanta
781
810
Dallas
661
239

Houston 1187

Miami

Figure 23.1
The graph models the distances among the cities.

The preceding chapter introduced the concept of graphs. You learned how
to represent edges using edge arrays, edge lists, adjacency matrices,
and adjacency lists, and how to model a graph using the Graph
interface, the AbstractGraph class, and the UnweightedGraph class. The
preceding chapter also introduced two important techniques for
traversing graphs: depth-first search and breadth-first search, and
applied traversal to solve practical problems. This chapter will
introduce weighted graphs. You will learn the algorithm for finding a
minimum spanning tree in §23.4 and the algorithm for finding shortest
paths in §23.5.

Pedagogical NOTE
<side remark: graph learning tool>
Before we introduce the algorithms and applications for
weighted graphs, it is helpful to get acquainted with
weighted graphs using an interactive tool from
www.cs.armstrong.edu/liang/animation/WeightedGraphLearnin
gTool.html, as shown in Figure 23.2. The tool allows you
to enter vertices, specify edges and their weights, view

214
© Copyright 2012 by Pearson Education, Inc. All Rights Reserved.
the graph, and find a MST and all shortest paths from a
single source, as shown in Figure 23.2.

Figure 23.2
You can use the tool to create a weighted graph with mouse
gestures and show MST and shortest paths.

23.2 Representing Weighted Graphs

<key point>
Often it is desirable to use a priority queue to store weighted
edges.
<end key point>
There are two types of weighted graphs: vertex weighted and edge
weighted. In a vertex-weighted graph, each vertex is assigned a weight.
In an edge-weighted graph, each edge is assigned a weight. Of the two
types, edge-weighted graphs have more applications. This chapter
considers edge-weighted graphs.

Weighted graphs can be represented in the same way as unweighted


graphs, except that you have to represent the weights on the edges. As
with unweighted graphs, the vertices in weighted graphs can be stored
in an array. This section introduces three representations for the
edges in weighted graphs.

23.2.1 Representing Weighted Edges: Edge List

Weighted edges can be represented using a two-dimensional array. For


example, you can store all the edges in the graph in Figure 23.3a using
the array in Figure 23.3b:

215
© Copyright 2012 by Pearson Education, Inc. All Rights Reserved.
vertex weight

1 2 edges = [[0, 1, 2], [0, 3, 8],


7
[1, 0, 2], [1, 2, 7], [1, 3, 3],
2
3 4 5 [2, 1, 7], [2, 3, 4], [2, 4, 5],
8 [3, 0, 8], [3, 1, 3], [3, 2, 4], [3, 4, 6],
6
[4, 2, 5], [4, 3, 6]
0 3 4 ]
(a) (b)

Figure 23.3
Each edge is assigned a weight on an edge-weighted graph.

23.2.2 Weighted Adjacency Matrices

Assume that the graph has n vertices. You can use a two-dimensional n ×
n matrix, say weights, to represent the weights on edges. weights[i][j]
represents the weight on edge (i, j). If vertices i and j are not
connected, weights[i][j] is null. For example, the weights in the graph
in Figure 23.3a can be represented using an adjacency matrix as
follows:

adjacencyMatrix = [ 0 1 2 3 4
[None, 2, None, 8, None], 0 None 2 None 8 None
[2, None, 7, 3, None],
1 2 None 7 3 None
[None, 7, None, 4, 5],
2 None 7 None 4 5
[8, 3, 4, None, 6],
[None, None, 5, 6, None] 3 8 3 4 None 6
] 4 None None 5 6 None

23.2.3 Priority Adjacency Lists

Another way to represent the edges is to define edges as objects, as


shown in Listing 23.1.

Listing 23.1 WeightedEdge.py


<Side Remark line 3: vertex index u>
<Side Remark line 4: vertex index v>
<Side Remark line 5: weight on edge>
<Side Remark line 6: compare weights>
1 class WeightedEdge:
2 def __init__(self, u, v, weight):
3 self.u = u
4 self.v = v
5 self.weight = weight # The weight on edge (u, v)
6
7 # Overload the comparison operators
8 # Note we purposely reverse the order
9 def __lt__(self, other):
10 return self.weight > other.weight
11
12 def __le__(self, other):
13 return self.weight >= other.weight
14
15 def __gt__(self, other):
16 return self.weight < other.weight
216
© Copyright 2012 by Pearson Education, Inc. All Rights Reserved.
17
18 def __ge__(self, other):
19 return self.weight <= other.weight
20
21 def __eq__(self, other):
22 return self.weight == other.weight
23
24 def __ne__(self, other):
25 return self.weight != other.weight

To create a WeightedEdge object, use WeightedEdge(i, j, w), where w is


the weight on edge (i, j). Often it is desirable to store a vertex’s
adjacent edges in a priority queue so that you can remove the edges in
increasing order of their weights. For this reason, the comparison
operators are implemented in the WeightedEdge class.

For unweighted graphs, we use adjacency lists to represent edges. For


weighted graphs, we still use adjacency lists, but the lists are
priority queues. A priority queue is implemented using a heap. We will
use the heap to store the adjacent weighted edges. For example, the
adjacency lists for the vertices in the graph in Figure 23.3 can be
represented as follows:
queues = [ five heaps in this list ]
queues[0] WeightedEdge(0, 1, 2) WeightedEdge(0, 3, 8)
queues[1] WeightedEdge(1, 0, 2) WeightedEdge(1, 3, 3) WeightedEdge(1, 2, 7)
queues[2] WeightedEdge(2, 3, 4) WeightedEdge(2, 4, 5) WeightedEdge(2, 1, 7)
queues[3] WeightedEdge(3, 1, 3) WeightedEdge(3, 2, 4) WeightedEdge(3, 4, 6) WeightedEdge(3, 0, 8)
queues[4] WeightedEdge(4, 2, 5) WeightedEdge(4, 3, 6)

queues[i] is a heap that stores all edges adjacent to vertex i.

23.3 The WeightedGraph Class

<key point>
WeightedGraph extends AbstractGraph.
<end key point>
The preceding chapter designed the Graph class for modeling graphs. We
design WeightedGraph as a subclass of Graph, as shown in Figure 23.4.

217
© Copyright 2012 by Pearson Education, Inc. All Rights Reserved.
Graph

WeightedGraph
queues[i] is a heap that contains all the weighted edges
queues: list
adjacent to vertex i.

WeightedGraph(vertices: list, edges: list) Constructs a weighted graph with the specified vertices and
edges.
getQueueForWeightedEdges(edges ): list Creates a priority queue and returns it.
printWeightedEdges(): void Displays all edges and weights.
getWeightedEdges(): list Returns all weighted edges for each vertex in a p riority
queue.

clear(): void Removes all vertices and edges from the graph.
addVertex(v : V): void Adds a vertex to the graph.
addEdge(u: int, v: int, weight: d oub le): void Adds a weighted edge to the grap h.
getM inimumSpanningTree(): MST Returns a min imum spanning tree starting from vertex 0.
getM inimumSpanningTreeAt(index: int): MST Returns a min imum spanning tree starting from vertex v.
getShortestPath(index: int): ShortestPathTree Returns all single-source shortes t paths.

Figure 23.4
WeightedGraph extends Graph.

WeightedGraph simply extends Graph with a constructor for creating a


WeightedGraph with a list of vertices and edge list. WeightedGraph
inherits all methods from Graph, overrides the clear, addVertex, and
addEdge methods, and also introduces the new methods for obtaining
minimum spanning trees and for finding single-source all shortest
paths. Minimum spanning trees and shortest paths will be introduced in
§23.4 and §23.5, respectively.

Listing 23.2 implements WeightedGraph.

Listing 23.2 WeightedGraph.py


<Side Remark line 8: extends Graph>
<Side Remark line 9: constructor>
<Side Remark line 10: invoke superclass constructor>
<Side Remark line 11: priority queues>
<Side Remark line 13: queues for weighted edges>
<Side Remark line 29: printWeightedEdges>
<Side Remark line 39: getWeightedEdges>
<Side Remark line 43: clear>
<Side Remark line 49: addVertex>
<Side Remark line 63: addEdge>
<Side Remark line 59: minimum spanning tree>
1 from Graph import Graph
2 from Graph import Tree
3 from WeightedEdge import WeightedEdge
4 from Heap import Heap # Heap is covered in Chapter 11
5
6 MAX_VALUE = 1e+308 # Infinity
7
8 class WeightedGraph(Graph):
9 def __init__(self, vertices, edges):
10 Graph.__init__(self, vertices, getEdges(edges))
11 self.queues = self.getQueueForWeightedEdge(edges)
12
13 def getQueueForWeightedEdge(self, edges):
218
© Copyright 2012 by Pearson Education, Inc. All Rights Reserved.
14 queues = []
15 for i in range(len(self.getVertices())):
16 # Each element in the queue is a heap
17 queues.append(Heap())
18
19 for i in range(len(edges)):
20 u = edges[i][0]
21 v = edges[i][1]
22 weight = edges[i][2]
23 # Insert an edge into the heap
24 queues[u].add(WeightedEdge(u, v, weight))
25
26 return queues
27
28 #Display edges with weights
29 def printWeightedEdges(self):
30 for i in range(len(self.queues)):
31 print(str(self.getVertex(i))
32 + " (" + str(i), end = "): ")
33 for edge in self.queues[i].getLst():
34 print("(" + str(edge.u) + ", " + str(edge.v)
35 + ", " + str(edge.weight), end = ") ")
36 print()
37
38 # Get the edges from the weighted graph
39 def getWeightedEdges(self):
40 return self.queues
41
42 # Clears the weighted graph
43 def clear(self):
44 Graph.vertices.clear()
45 Graph.neighbors.clear()
46 self.queues.clear()
47
48 # Add vertices to the weighted graph
49 def addVertex(vertex):
50 Graph.addVertex(vertex)
51 queues.add(Heap())
52
53 # Add edges to the weighted graph
54 def addEdge(u, v, weight):
55 Graph.addEdge(u, v);
56 queues[u].add(WeightedEdge(u, v, weight))
57
58 # Get a minimum spanning tree rooted at vertex 0 */
59 def getMinimumSpanningTree(self):
60 return self.getMinimumSpanningTreeAt(0)
61
62 # Get a minimum spanning tree rooted at a specified vertex
63 def getMinimumSpanningTreeAt(self, startingIndex):
64 # T initially contains the startingVertex;
65 T = [startingIndex]
66
67 numberOfVertices = len(self.vertices) # Number of vertices
68 # Initially set the parent of all vertices to -1
69 parent = numberOfVertices * [-1] # Parent of a vertex
70
71 totalWeight = 0 # Total weight of the tree thus far
72
73 # Clone the queues, so to keep the original queue intact
74 queues = deepClone(self.queues);
75

219
© Copyright 2012 by Pearson Education, Inc. All Rights Reserved.
76 # All vertices are found?
77 while len(T) < numberOfVertices:
78 # Search for the vertex with the smallest edge
79 # adjacent to a vertex in T
80 v = -1
81 smallestWeight = MAX_VALUE;
82 for u in T:
83 while not queues[u].isEmpty() and \
84 queues[u].peek().v in T:
85 # Remove the edge from queues[u] if the adjacent
86 # vertex of u is already in T
87 queues[u].remove()
88
89 if queues[u].isEmpty():
90 continue # Consider the next vertex in T
91
92 # Current smallest weight on an edge adjacent to u
93 edge = queues[u].peek()
94 if edge.weight < smallestWeight:
95 v = edge.v
96 smallestWeight = edge.weight
97 # u is the parent for v
98 parent[v] = u
99
100 if v != -1:
101 T.append(v) # Add a new vertex to the tree
102 else:
103 # The tree is not connected, a partial MST is found
104 break
105
106 totalWeight += smallestWeight
107
108 return MST(startingIndex, parent, T,
109 totalWeight, self.vertices)
110
111 # Find single source shortest paths
112 def getShortestPath(self, sourceIndex):
113 # T stores the vertices whose path found so far
114 T = [sourceIndex]
115
116 numberOfVertices = len(self.vertices)
117
118 # parent[v] stores the previous vertex of v in the path
119 # The parent of source is set to -1
120 parent = numberOfVertices * [-1]
121
122 # costs[v] stores the cost of the path from v to the source
123 # Initial cost set to infinity
124 costs = numberOfVertices * [MAX_VALUE]
125 costs[sourceIndex] = 0 # Cost of source is 0
126
127 # Get a copy of queues
128 queues = deepClone(self.queues)
129
130 # Expand T
131 while len(T) < numberOfVertices:
132 v = -1 # Vertex to be determined
133 smallestCost = MAX_VALUE # Set to infinity
134 for u in T:
135 while (not queues[u].isEmpty() and
136 queues[u].peek().v in T):
137 # Remove the vertex in queue for u

220
© Copyright 2012 by Pearson Education, Inc. All Rights Reserved.
138 queues[u].remove()
139
140 if queues[u].isEmpty():
141 # All vertices adjacent to u are in T
142 continue
143
144 e = queues[u].peek()
145 if costs[u] + e.weight < smallestCost:
146 v = e.v
147 smallestCost = costs[u] + e.weight
148 # now u is the parent for v
149 parent[v] = u
150
151 T.append(v) # Add a new vertex to T
152 costs[v] = smallestCost
153
154 # Create a ShortestPathTree
155 return ShortestPathTree(sourceIndex, parent, T, costs,
156 self.vertices)
157
158 # Clone queues
159 def deepClone(queues):
160 copiedQueues = []
161
162 for i in range(len(queues)):
163 copiedQueues.append(Heap())
164 for e in queues[i].getLst():
165 copiedQueues[i].add(e)
166
167 return copiedQueues
168
169 # MST is a subclass of Tree, defined in the preceding chapter
170 class MST(Tree):
171 def __init__(self, startingIndex, parent, T,
172 totalWeight, vertices):
173 Tree.__init__(self, startingIndex, parent, T, vertices)
174 # Total weight of all edges in the tree
175 self.totalWeight = totalWeight
176
177 def getTotalWeight(self):
178 return self.totalWeight
179
180 # ShortestPathTree is an inner class in WeightedGraph
181 class ShortestPathTree(Tree):
182 def __init__(self, sourceIndex, parent, T, costs, vertices):
183 Tree.__init__(self, sourceIndex, parent, T, vertices)
184 self.costs = costs
185
186 # Return the cost for a path from the root to vertex v
187 def getCost(self, v):
188 return self.costs[v]
189
190 # Print paths from all vertices to the source
191 def printAllPaths(self):
192 print("All shortest paths from "
193 + str(self.vertices[self.root]) + " are:")
194 for i in range(len(self.costs)):
195 self.printPath(i) # Print a path from i to the source
196 print("(cost: " + str(self.costs[i]) + ")") # Path cost
197
198 def getEdges(edges):
199 edgeList = []

221
© Copyright 2012 by Pearson Education, Inc. All Rights Reserved.
200
201 for i in range(len(edges)):
202 u = edges[i][0]
203 v = edges[i][1]
204 # Insert an edge into the heap
205 edgeList.append([u, v])
206
207 return edgeList

When a WeightedGraph is constructed, Graph.__init__(self, vertices,


getEdges(edges)) is invoked to initialize the data fields in the
superclass Graph (line 10). queues[i] is used internally to store
adjacent weighted edges for a vertex i.

The getEdges(edges) function (lines 198-207) creates an edge list and


it is used for creating an unweighted graph (line 10). The
getQueueForWeightedEdge(edges) method (lines 13-26) creates an weighted
edge list and it is assigned to self.queues (line 11).

The methods getMinimumSpanningTree() (lines 59–60),


getMinimumSpanningTreeAt(startingIndex) (lines 63–109), and
getShortestPaths() (lines 112–156) will be introduced in the upcoming
sections.

Listing 23.3 gives a test program that creates a graph for the one in
Figure 23.1 and another graph for the one in Figure 23.3a.

Listing 23.3 TestWeightedGraph.py


<Side Remark line 4: vertices>
<Side Remark line 9: edges>
<Side Remark line 29: create a graph>
<Side Remark line 34: print edges>
<Side Remark line 37: vertices>
<Side Remark line 38: edges>
<Side Remark line 45: create a graph>
<Side Remark line 47: print edges>
1 from WeightedGraph import WeightedGraph
2
3 # Create vertices
4 vertices = ["Seattle", "San Francisco", "Los Angeles",
5 "Denver", "Kansas City", "Chicago", "Boston", "New York",
6 "Atlanta", "Miami", "Dallas", "Houston"];
7
8 # Create edges
9 edges = [
10 [0, 1, 807], [0, 3, 1331], [0, 5, 2097],
11 [1, 0, 807], [1, 2, 381], [1, 3, 1267],
12 [2, 1, 381], [2, 3, 1015], [2, 4, 1663], [2, 10, 1435],
13 [3, 0, 1331], [3, 1, 1267], [3, 2, 1015], [3, 4, 599],
14 [3, 5, 1003],
15 [4, 2, 1663], [4, 3, 599], [4, 5, 533], [4, 7, 1260],
16 [4, 8, 864], [4, 10, 496],
17 [5, 0, 2097], [5, 3, 1003], [5, 4, 533],
18 [5, 6, 983], [5, 7, 787],
19 [6, 5, 983], [6, 7, 214],
20 [7, 4, 1260], [7, 5, 787], [7, 6, 214], [7, 8, 888],
21 [8, 4, 864], [8, 7, 888], [8, 9, 661],
22 [8, 10, 781], [8, 11, 810],
222
© Copyright 2012 by Pearson Education, Inc. All Rights Reserved.
23 [9, 8, 661], [9, 11, 1187],
24 [10, 2, 1435], [10, 4, 496], [10, 8, 781], [10, 11, 239],
25 [11, 8, 810], [11, 9, 1187], [11, 10, 239]
26 ]
27
28 # Create a graph
29 graph1 = WeightedGraph(vertices, edges)
30 print("The number of vertices in graph1: " + str(graph1.getSize()))
31 print("The vertex with index 1 is " + graph1.getVertex(1))
32 print("The index for Miami is " + str(graph1.getIndex("Miami")))
33 print("The edges for graph1: ")
34 graph1.printWeightedEdges()
35
36 # Create vertices and edges
37 vertices = [x for x in range(5)]
38 edges = [
39 [0, 1, 2], [0, 3, 8],
40 [1, 0, 2], [1, 2, 7], [1, 3, 3],
41 [2, 1, 7], [2, 3, 4], [2, 4, 5],
42 [3, 0, 8], [3, 1, 3], [3, 2, 4], [3, 4, 6],
43 [4, 2, 5], [4, 3, 6]
44 ]
45 graph2 = WeightedGraph(vertices, edges) # Create a graph
46 print("\nThe edges for graph2:")
47 graph2.printWeightedEdges()

<Output>
The number of vertices in graph1: 12
The vertex with index 1 is San Francisco
The index for Miami is 9
The edges for graph1:
Vertex 0: (0, 1, 807) (0, 3, 1331) (0, 5, 2097)
Vertex 1: (1, 2, 381) (1, 0, 807) (1, 3, 1267) Formatted: English (U.S.)
Vertex 2: (2, 1, 381) (2, 3, 1015) (2, 4, 1663) (2, 10, 1435)
Vertex 3: (3, 4, 599) (3, 5, 1003) (3, 1, 1267)
(3, 0, 1331) (3, 2, 1015)
Vertex 4: (4, 10, 496) (4, 8, 864) (4, 5, 533) (4, 2, 1663)
(4, 7, 1260) (4, 3, 599)
Vertex 5: (5, 4, 533) (5, 7, 787) (5, 3, 1003)
(5, 0, 2097) (5, 6, 983)
Vertex 6: (6, 7, 214) (6, 5, 983)
Vertex 7: (7, 6, 214) (7, 8, 888) (7, 5, 787) (7, 4, 1260)
Vertex 8: (8, 9, 661) (8, 10, 781) (8, 4, 864)
(8, 7, 888) (8, 11, 810)
Vertex 9: (9, 8, 661) (9, 11, 1187)
Vertex 10: (10, 11, 239) (10, 4, 496) (10, 8, 781) (10, 2, 1435)
Vertex 11: (11, 10, 239) (11, 9, 1187) (11, 8, 810)

The edges for graph2:


Vertex 0: (0, 1, 2) (0, 3, 8) Formatted: English (U.S.)
Vertex 1: (1, 0, 2) (1, 2, 7) (1, 3, 3)
Vertex 2: (2, 3, 4) (2, 1, 7) (2, 4, 5)
Vertex 3: (3, 1, 3) (3, 4, 6) (3, 2, 4) (3, 0, 8)
Vertex 4: (4, 2, 5) (4, 3, 6)
<End Output>
The program creates graph1 for the graph in Figure 23.1 in lines 3-29.
The vertices for graph1 are defined in lines 4–6. The edges for graph1
are defined in lines 9–26. The edges are represented using a nested
223
© Copyright 2012 by Pearson Education, Inc. All Rights Reserved.
list. edges[i][0] and edges[i][1] indicate that there is an edge from
vertex edges[i][0] to vertex edges[i][1] and the weight for the edge is
edges[i][2]. For example, the first row [0, 1, 807] represents the edge
from vertex 0 (edges[0][0]) to vertex 1 (edges[0][1]) with weight 807
(edges[0][2]). The row [0, 5, 2097] represents the edge from vertex 0
(edges[2][0]) to vertex 5 (edges[2][1]) with weight 2097 (edges[2][2]).
Line 35 invokes the printWeightedEdges() method on graph1 to display
all edges in graph1.

The program creates the edges for graph2 for the graph in Figure 23.3a
in lines 37–45. Line 47 invokes the printWeightedEdges() method on
graph2 to display all edges in graph2.

23.4 Minimum Spanning Trees

<key point>
A minimum spanning tree of a graph is a spanning tree with the
minimum total weights.
<end key point>
A graph may have many spanning trees. Suppose that the edges are
weighted. A minimum spanning tree has the minimum total weights. For
example, the trees in Figures 23.3b, 23.3c, 23.3d are spanning trees
for the graph in Figure 23.5a. The trees in Figures 23.3c and 23.3d are
minimum spanning trees.

10 10

6 5 8
7 5

8 10

5 7 7 8 5 7 7 8

12

(a) (b)

6 6 5
5
7

5 7 8 5 7 7 8

(c) (d)

Figure 23.5
The tree in (c) and (d) are minimum spanning trees of the graph in (a).

The problem of finding a minimum spanning tree has many applications.


Consider a company with branches in many cities. The company wants to
lease telephone lines to connect all branches together. The phone
company charges different amounts of money to connect different pairs
of cities. There are many ways to connect all branches together. The
cheapest way is to find a spanning tree with the minimum total rates.
224
© Copyright 2012 by Pearson Education, Inc. All Rights Reserved.
23.4.1 Minimum Spanning Tree Algorithms

<Side Remark: Prim’s algorithm>


How do you find a minimum spanning tree? There are several well-known
algorithms for doing so. This section introduces Prim’s algorithm.
Prim’s algorithm starts with a spanning tree T that contains an
arbitrary vertex. The algorithm expands the tree by adding a vertex
with the smallest edge incident to a vertex already in the tree. The
algorithm is described in Listing 23.4.

Listing 23.4 Prim’s Minimum Spanning Tree Algorithm


<Side Remark line 4: add initial vertex>
<Side Remark line 6: more vertices?>
<Side Remark line 7: find a vertex>
<Side Remark line 9: add to tree>
1 def minimumSpanningTree():
2 Let V denote the set of vertices in the graph;
3 Let T be a set for the vertices in the spanning tree;
4 Initially, add the starting vertex to T;
5
6 while size of T < n:
7 find u in T and v in V – T with the smallest weight
8 on the edge (u, v), as shown in Figure 23.6;
9 add v to T;

Vertices already in Vertices not currently in


V-T the spanning tree
the spanning tree
T

Figure 23.6
Find a vertex u in T that connects a vertex v in V – T with the
smallest weight.

The algorithm starts by adding the starting vertex into T. It then


continuously adds a vertex (say v) from V – T into T. v is the vertex
that is adjacent to a vertex in T with the smallest weight on the edge.
For example, there are five edges connecting vertices in T and V – T,
as shown in Figure 23.6, (u, v) is the one with the smallest weight.

<Side Remark: example>


Consider the graph in Figure 23.7. The algorithm adds the vertices to T
in this order:

1. Add vertex 0 to T.
2. Add vertex 5 to T, since Edge(5, 0, 5) has the smallest weight
among all edges incident to a vertex in T, as shown in Figure
23.7a.

225
© Copyright 2012 by Pearson Education, Inc. All Rights Reserved.
3. Add vertex 1 to T, since Edge(1, 0, 6) has the smallest weight
among all edges incident to a vertex in T, as shown in Figure
23.7b.
4. Add vertex 6 to T, since Edge(6, 1, 7) has the smallest weight
among all edges incident to a vertex in T, as shown in Figure
23.7c.
5. Add vertex 2 to T, since Edge(2, 6, 5) has the smallest weight
among all edges incident to a vertex in T, as shown in Figure
23.7d.
6. Add vertex 4 to T, since Edge(4, 6, 7) has the smallest weight
among all edges incident to a vertex in T, as shown in Figure
23.7e.
7. Add vertex 3 to T, since Edge(3, 2, 8) has the smallest weight
among all edges incident to a vertex in T, as shown in Figure
23.7f.

1 10 2 1 10 2
6 5 8 6
7 7 5 8

0 8 6 10 3 0 8 6 10 3

5 7 7 8 5 7 7 8

5 12 4 5 12 4

(a) (b)

1 10 1 10 2
2

6 8
6 5 8
7 5 7

0 8 6 10 3 0 8 6 10 3

5 7 7 8 5 7 7 8

5 12 4 5 12 4

(c) (d)

1 10 2 1 10 2

6 5 8 6 5 8
7 7

0 8 6 10 3 0 8 6 10 3

5 7 7 8 5 7 7 8

5 12 4 5 12 4

(e) (f)

Figure 23.7
The adjacent vertices with the smallest weight are added successively
to T.

NOTE:
<Side Remark: unique tree?>
A minimum spanning tree is not unique. For example, both
(c) and (d) in Figure 23.7 are minimum spanning trees for

226
© Copyright 2012 by Pearson Education, Inc. All Rights Reserved.
the graph in Figure 23.7a. However, if the weights are
distinct, the graph has a unique minimum spanning tree.

NOTE:
<Side Remark: connected and undirected>
Assume that the graph is connected and undirected. If a
graph is not connected or directed, the algorithm will
not work. You may modify the algorithm to find a spanning
forest for any undirected graph.

23.4.2 Implementation of the MST Algorithm

<Side Remark: getMinimumSpanningTree()>


The getMinimumSpanningTree(int v) method is defined in the
WeightedGraph class. It returns an instance of the MST class, as shown
in Figure 23.4. The MST class is defined as an inner class in the
WeightedGraph class, which extends the Tree class, as shown in Figure
23.8. The Tree class was shown in Figure 23.11. The MST class was
implemented in lines 178–190 in Listing 23.2.

Tree

MST
totalWeight: int Total weight of the tree.

M ST(root: int, parent: list, searchOrder: list, Cons tructs an MST with the specified root, parent array,
totalWeight: int, vertices: lis t) searchOrder, total weight for the tree, and vertices.
getTotalWeight(): in t Returns the totalWeight of the tree.

Figure 23.8
The MST class extends the Tree class.

The getMinimumSpanningTree method was implemented in lines 59-60 in


Listing 23.2. The getMinimumSpanningTreeAt(int startingVertex) method
first adds startingVertex to T (line 65). T is a list that stores the
vertices added into the spanning tree. vertices is defined as a data
field in the Graph class, which is a list that stores all vertices in
the graph. len(vertices) returns the number of the vertices in the
graph (line 67).

A vertex is added to T if it is adjacent to one of the vertices in T


with the smallest weight (line 101). Such a vertex is found using the
following procedure:

1. For each vertex u in T, find its neighbor with the smallest


weight to u. All the neighbors of u are stored in queues[u].
queues[u].peek() (line 84) returns the adjacent edge with the
smallest weight. If a neighbor is already in T, remove it (line
87). To keep the original queues intact, a copy is created in
line 74. After lines 83–90, queues[u].peek() (line 93) returns
the vertex with the smallest weight to u.
2. Compare all these neighbors and find the one with the smallest
weight (lines 93–98).

227
© Copyright 2012 by Pearson Education, Inc. All Rights Reserved.
After a new vertex is added to T (line 101), totalWeight is updated
(line 106). Once all vertices are added to T, an instance of MST is
created (line 108). Note that the method will not work if the graph is
not connected. However, you can modify it to obtain a partial MST.

The MST class extends the Tree class (line 170). To create an instance
of MST, pass root, parent, T, totalWeight, and vertices (lines 108-
109). The data fields root, parent, searchOrder, vertices, are defined
in the Tree class, which is in the Graph module in the preceding
chapter.

<Side Remark: time complexity>


For each vertex, the program constructs a priority queue for its
adjacent edges. It takes O(log|V|) time to insert an edge into a
priority queue and the same time to remove an edge from the priority
queue. So the overall time complexity for the program is O (| E | log | V |) ,
where |E| denotes the number of edges and |V| the number of vertices.

Listing 23.5 gives a test program that displays minimum spanning trees
for the graph in Figure 23.1 and the graph in Figure 23.3a,
respectively.

Listing 23.5 TestMinimumSpanningTree.py


<Side Remark line 1: import WeightedGraph>
<Side Remark line 4: create vertices>
<Side Remark line 9: create edges>
<Side Remark line 29: create graph1>
<Side Remark line 32: MST for graph1>
<Side Remark line 33: total weight>
<Side Remark line 34: print tree>
<Side Remark line 37: create vertices>
<Side Remark line 38: create edges>
<Side Remark line 41: create graph2>
<Side Remark line 47: MST for graph2>
<Side Remark line 51: total weight>
<Side Remark line 52: print tree>
1 from WeightedGraph import WeightedGraph
2
3 # Create vertices
4 vertices = ["Seattle", "San Francisco", "Los Angeles",
5 "Denver", "Kansas City", "Chicago", "Boston", "New York",
6 "Atlanta", "Miami", "Dallas", "Houston"]
7
8 # Create edges
9 edges = [
10 [0, 1, 807], [0, 3, 1331], [0, 5, 2097],
11 [1, 0, 807], [1, 2, 381], [1, 3, 1267],
12 [2, 1, 381], [2, 3, 1015], [2, 4, 1663], [2, 10, 1435],
13 [3, 0, 1331], [3, 1, 1267], [3, 2, 1015], [3, 4, 599],
14 [3, 5, 1003],
15 [4, 2, 1663], [4, 3, 599], [4, 5, 533], [4, 7, 1260],
16 [4, 8, 864], [4, 10, 496],
17 [5, 0, 2097], [5, 3, 1003], [5, 4, 533],
18 [5, 6, 983], [5, 7, 787],
19 [6, 5, 983], [6, 7, 214],
20 [7, 4, 1260], [7, 5, 787], [7, 6, 214], [7, 8, 888],
21 [8, 4, 864], [8, 7, 888], [8, 9, 661],
228
© Copyright 2012 by Pearson Education, Inc. All Rights Reserved.
22 [8, 10, 781], [8, 11, 810],
23 [9, 8, 661], [9, 11, 1187],
24 [10, 2, 1435], [10, 4, 496], [10, 8, 781], [10, 11, 239],
25 [11, 8, 810], [11, 9, 1187], [11, 10, 239]
26 ]
27
28 # Create a graph
29 graph1 = WeightedGraph(vertices, edges)
30
31 # Obtain a minimum spanning tree
32 tree1 = graph1.getMinimumSpanningTree()
33 print("Total weight is " + str(tree1.getTotalWeight()))
34 tree1.printTree()
35
36 # Create vertices and edges
37 vertices = [x for x in range(5)]
38 edges = [
39 [0, 1, 2], [0, 3, 8],
40 [1, 0, 2], [1, 2, 7], [1, 3, 3],
41 [2, 1, 7], [2, 3, 4], [2, 4, 5],
42 [3, 0, 8], [3, 1, 3], [3, 2, 4], [3, 4, 6],
43 [4, 2, 5], [4, 3, 6]
44 ]
45
46 # Create a graph
47 graph2 = WeightedGraph(vertices, edges)
48
49 # Obtain a minimum spanning tree
50 tree2 = graph2.getMinimumSpanningTreeAt(1)
51 print("Total weight is " + str(tree2.getTotalWeight()))
52 tree2.printTree()

<Output>
Total weight is 6513.0
Root is: Seattle
Edges: (Seattle, San Francisco) (San Francisco, Los Angeles)
(Los Angeles, Denver) (Denver, Kansas City) (Kansas City, Chicago)
(New York, Boston) (Chicago, New York) (Dallas, Atlanta)
(Atlanta, Miami) (Kansas City, Dallas) (Dallas, Houston)

Total weight is 14.0


Root is: 1
Edges: (1, 0) (3, 2) (1, 3) (2, 4)
<End Output>
The program creates a weighted graph for Figure 23.1 in line 29. It
then invokes getMinimumSpanningTree() (line 32) to return an MST that
represents a minimum spanning tree for the graph. Invoking printTree()
(line 34) on the MST object displays the edges in the tree. Note that
MST is a subclass of Tree. The printTree() method is defined in the
Tree class.

<Side Remark: graphical illustration>


The graphical illustration of the minimum spanning tree is shown in
Figure 23.9. The vertices are added to the tree in this order: Seattle,
San Francisco, Los Angeles, Denver, Kansas City, Dallas, Houston,
Chicago, New York, Boston, Atlanta, and Miami.

229
© Copyright 2012 by Pearson Education, Inc. All Rights Reserved.
Seattle

2097 Boston
1 983
Chicago
1331 9 214
807 8
1003 New York
787
7
Denver
533
1267 1260
599
3 888
4
San Francisco 1015
Kansas City
381 1663
2 864

496
Los Angeles 1435 10 Atlanta
5 781
810
Dallas
6 661
239

Houston 1187 11
Miami

Figure 23.9
The edges in the minimum spanning tree for the cities are highlighted.

<check point>
23.1
Find a minimum spanning tree for the following graph.

1 10 2

5 7 8
7

0 2 6 10 3

5 7 7 8

5 2 4

23.2
Is the minimum spanning tree unique if all edges have different
weights?

23.3
If you use an adjacency matrix to represent weighted edges, what will
be the time complexity for Prim’s algorithm?

23.4
What happens to the getMinimumSpanningTree() method in WeightedGraph if
the graph is not connected? Verify your answer by writing a test
program that creates an unconnected graph and invokes the
getMinimumSpanningTree() method. How do you fix the problem by
obtaining a partial MST?
<end check point>
23.5 Finding Shortest Paths

<key point>
A shortest path between two vertices is the path with the minimum
total weights.
<end key point>
230
© Copyright 2012 by Pearson Education, Inc. All Rights Reserved.
Given a graph with nonnegative weights on the edges, a well-known
algorithm for finding a single-source shortest path was discovered by
Edsger Dijkstra, a Dutch computer scientist. Dijkstra’s algorithm uses
costs[v] to store the cost of the shortest path from vertex v to the
source vertex s. So costs[s] is 0. Initially assign infinity to
costs[v] to indicate that no path is found from v to s. Let V denote
all vertices in the graph and T denote the set of the vertices whose
costs have been found so far. Initially, the source vertex s is in T.
The algorithm repeatedly finds a vertex u in T and a vertex v in V – T
such that costs[u] + w(u, v) is the smallest, and moves v to T. Here
w(u, v) denotes the weight on edge (u, v).

The algorithm is described in Listing 23.6.

Listing 23.6 Dijkstra’s Single-Source Shortest-Path Algorithm


<Side Remark line 5: add initial vertex>
<Side Remark line 7: more vertex>
<Side Remark line 8: find next vertex>
<Side Remark line 10: add a vertex>
1 def shortestPath(s):
2 Let V denote the set of vertices in the graph;
3 Let T be a set that contains the vertices whose
4 paths to s have been found
5 Initially T contains source vertex s with costs[s] = 0
6
7 while size of T < n:
8 find v in V – T with the smallest costs[u] + w(u, v) value
9 among all u in T
10 add v to T and costs[v] = costs[u] + w(u, v)

This algorithm is very similar to Prim’s for finding a minimum spanning


tree. Both algorithms divide the vertices into two sets T and V - T. In
the case of Prim’s algorithm, set T contains the vertices that are
already added to the tree. In the case of Dijkstra’s, set T contains
the vertices whose shortest paths to the source have been found. Both
algorithms repeatedly find a vertex from V – T and add it to T. In the
case of Prim’s algorithm, the vertex is adjacent to some vertex in the
set with the minimum weight on the edge. In the case of Dijkstra’s, the
vertex is adjacent to some vertex in the set with the minimum total
cost to the source.

T contai ns vertices whose V- T contains verti ces whose sh ortest


shortest path to s have been V-T path to s have not b een found
found

T
v

s u

Figure 23.10
Find a vertex u in T that connects a vertex v in V – T with the
smallest costs[u] + w(u, v).
231
© Copyright 2012 by Pearson Education, Inc. All Rights Reserved.
The algorithm starts by adding the source vertex s into T and sets
costs[s] to 0 (line 5). It then continuously adds a vertex (say v) from
V – T into T. v is the vertex that is adjacent to a vertex in T with
the smallest costs[u] + w(u, v). For example, there are five edges
connecting vertices in T and V – T, as shown in Figure 23.10; (u, v) is
the one with the smallest costs[u] + w(u, v). After v is added to T,
set costs[v] to costs[u] + w(u, v) (line 10).

Let us illustrate Dijkstra’s algorithm using the graph in Figure


23.11a. Suppose the source vertex is 1. So, costs[1] is 0 and the costs
for all other vertices are initially ∞, as shown in Figure 23.11b. We
use the parent[i] to denote the parent of i in the path. For
convenience, set the parent of the source node to -1.

1 10 3
costs
5 0
9 5 8
4 0 1 2 3 4 5 6
8 6 8
2 parent
1 4 7 5 -1
0 1 2 3 4 5 6
0 4 5

(a) (b)

Figure 23.11
The algorithm will find all shortest paths from source vertex 1.

Initially set T contains the source vertex. Vertices 2, 0, 6, and 3 are


adjacent to the vertices in T, and vertex 2 has the path of smallest
cost to source vertex 1. So add 2 to T. costs[2] now becomes 5, as
shown in Figure 23.12.

1 10 3
costs
5 0 5
9 5 8
4 0 1 2 3 4 5 6
8 6 8
2 parent
1 4 7 5 -1 1
0 1 2 3 4 5 6
0 4 5

(a) (b)

Figure 23.12
Now vertices 1 and 2 are in the set T.

Now T contains [1, 2]. Vertices 0, 6, and 3 are adjacent to the


vertices in T, and vertex 0 has a path of smallest cost to source
vertex 1. So add 1 to T. costs[0] now becomes 6, as shown in Figure
23.13.

232
© Copyright 2012 by Pearson Education, Inc. All Rights Reserved.
1 10 3
costs
5 6 0 5
9 5 8
4 0 1 2 3 4 5 6
8 6 8
2 parent
1 4 7 5 2 -1 1
0 1 2 3 4 5 6
0 4 5

(a) (b)

Figure 23.13
Now vertices [1, 2, 0] are in the set T.

Now T contains [1, 2, 0]. Vertices 3, 6, and 5 are adjacent to the


vertices in T, and vertex 6 has the path of smallest cost to source
vertex 1. So add 6 to T. costs[6] now becomes 9, as shown in Figure
23.14.

1 10 3
costs
5 6 0 5 9
9 5 8
4 0 1 2 3 4 5 6
8 6 8
2 parent
1 4 7 5 2 -1 1 1
0 1 2 3 4 5 6
0 4 5

(a) (b)

Figure 23.14
Now vertices [1, 2, 0, 6] are in the set T.

Now T contains [1, 2, 0, 6]. Vertices 3 and 5 are adjacent to the


vertices in T, and both vertices have a path of the same smallest cost
to source vertex 1. You can choose either 3 or 5. Let us add 3 to T.
costs[3] now becomes 10, as shown in Figure 23.15.

1 10 3
costs
5 6 0 5 10 9
9 5 8
4 0 1 2 3 4 5 6
8 6 8
2 parent
1 4 7 5 2 -1 1 1 1
0 1 2 3 4 5 6
0 4 5

(a) (b)

Figure 23.15
Now vertices [1, 2, 0, 6, 3] are in the set T.

233
© Copyright 2012 by Pearson Education, Inc. All Rights Reserved.
Now T contains [1, 2, 0, 6, 3]. Vertices 4 and 5 are adjacent to the
vertices in T, and vertex 5 has the path of smallest cost to source
vertex 1. So add 5 to T. costs[5] now becomes 10, as shown in Figure
23.16.

1 10 3
costs
5 6 0 5 10 10 9
9 5 8
4 0 1 2 3 4 5 6
8 6 8
2 parent
1 4 7 5 2 -1 1 1 0 1
0 1 2 3 4 5 6
0 4 5

(a) (b)

Figure 23.16
Now vertices [1, 2, 0, 6, 3, 5] are in the set T.

Now T contains [1, 2, 0, 6, 3, 5]. The smallest cost for a path to


connect 4 with 1 is 15, as shown in Figure 23.23.

1 10 3
costs
5 6 0 5 10 15 10 9
9 5 8
4 0 1 2 3 4 5 6
8 6 8
2 parent
1 4 7 5 2 -1 1 1 5 0 1
0 1 2 3 4 5 6
0 4 5

(a) (b)

Figure 23.17
Now vertices [1, 2, 6, 0, 3, 5, 4] are in set T.

<Side Remark: shortest-path tree>


As you see, the algorithm essentially finds all shortest paths from a
source vertex, which produces a tree rooted at the source vertex. We
call this tree a single-source all-shortest-path tree (or simply a
shortest-path tree). To model this tree, define a class named
ShortestPathTree that extends the Tree class, as shown in Figure 23.18.
ShortestPathTree is defined as an inner class in WeightedGraph in lines
249-273 in Listing 23.2.

234
© Copyright 2012 by Pearson Education, Inc. All Rights Reserved.
Tree

ShortestPathTree
costs: list costs[v] stores the cost for the path from th e source to v.

ShortestPathTree(s ource: int, parent: list, Constructs a shortest path tree with the s pecified s ource,
searchOrder: list, cos ts: list, vertices) parent array, and costs array.
getCost(vertexInd ex: int): int Returns th e cost for the path from the source to the vertex.
printAllPaths (): void Dis plays all paths from the s ource.

Figure 23.18
WeightedGraph.ShortestPathTree extends AbstractGraph.Tree.

The getShortestPath(sourceIndex) method was implemented in lines 112–


156 in Listing 23.2. The method first adds sourceIndex to T (line 114).
T is a list that stores the vertices whose path has been found.
vertices is defined as a data field in the Graph class, which is a list
that stores all vertices in the graph. len(vertices) returns the number
of the vertices in the graph (line 116).

Each vertex is assigned a cost. The cost of the source vertex is 0


(line 125). The cost of all other vertices is initially assigned
infinity (line 124).

The method needs to remove the elements from the queues in order to
find the one with the smallest total cost. To keep the original queues
intact, queues are cloned in line 128.

A vertex is added to T if it is adjacent to one of the vertices in T


with the smallest cost (line 151). Such a vertex is found using the
following procedure:

1. For each vertex u in T, find its incident edge e with the


smallest weight to u. All the incident edges to u are stored in
queues[u]. queues[u].peek() (line 136) returns the incident edge
with the smallest weight. If e.v is already in T, remove e from
queues[u] (line 138). After lines 135–138, queues[u].peek()
returns the edge e such that e has the smallest weight to u and
e.v is not in T (line 144).
2. Compare all these edges and find the one with the smallest value
on costs[u] + e.getWeight() (line 145).

After a new vertex is added to T (line 151), the cost of this vertex is
updated (line 152). Once all vertices are added to T, an instance of
ShortestPathTree is created (lines 155-156). Note that the method will
not work if the graph is not connected. However, you can modify it to
obtain the shortest paths to all connected vertices.

<Side Remark: ShortestPathTree class>


The ShortestPathTree class extends the Tree class (line 181). To create
an instance of ShortestPathTree, pass sourceVertex, parent, T, costs,
and vertices (lines 155-156). sourceVertex becomes the root in the
tree. The data fields root, parent, searchOrder, and vertices are
defined in the Tree class, which is defined in the Graph module in the
preceding chapter.

<Side Remark: Dijkstra’s algorithm time complexity>


235
© Copyright 2012 by Pearson Education, Inc. All Rights Reserved.
Dijkstra’s algorithm is implemented essentially in the same way as
Prim’s. So, the time complexity for Dijkstra’s algorithm is
O (| E | log | V |) , where |E| denotes the number of edges and |V| the number
of vertices.

<Side Remark: greedy and dynamic programming>


Dijkstra’s algorithm is a combination of greedy algorithm and dynamic
programming. It is a greedy algorithm in the sense that it always adds
a new vertex that has the shortest distance to the source. It stores
the shortest distance of each known vertex to the source and uses it
later to avoid redundant computing. So Dijkstra’s algorithm also uses
dynamic programming.

Listing 23.7 gives a test program that displays all shortest paths from
Chicago to all other cities in Figure 23.1 and all shortest paths from
vertex 3 to all vertices for the graph in Figure 23.1, respectively.

Listing 23.7 TestShortestPath.py


<Side Remark line 1: import WeightedGraph>
<Side Remark line 4: create vertices>
<Side Remark line 9: create edges>
<Side Remark line 29: create graph1>
<Side Remark line 32: shortest path>
<Side Remark line 33: print all paths>
<Side Remark line 37: get one path>
<Side Remark line 38: print path>
<Side Remark line 41: create vertices>
<Side Remark line 42: create edges>
<Side Remark line 51: create graph2>
<Side Remark line 54: shortest path>
<Side Remark line 52: print all paths>
1 from WeightedGraph import WeightedGraph
2
3 # Create vertices
4 vertices = ["Seattle", "San Francisco", "Los Angeles",
5 "Denver", "Kansas City", "Chicago", "Boston", "New York",
6 "Atlanta", "Miami", "Dallas", "Houston"]
7
8 # Create edges
9 edges = [
10 [0, 1, 807], [0, 3, 1331], [0, 5, 2097],
11 [1, 0, 807], [1, 2, 381], [1, 3, 1267],
12 [2, 1, 381], [2, 3, 1015], [2, 4, 1663], [2, 10, 1435],
13 [3, 0, 1331], [3, 1, 1267], [3, 2, 1015], [3, 4, 599],
14 [3, 5, 1003],
15 [4, 2, 1663], [4, 3, 599], [4, 5, 533], [4, 7, 1260],
16 [4, 8, 864], [4, 10, 496],
17 [5, 0, 2097], [5, 3, 1003], [5, 4, 533],
18 [5, 6, 983], [5, 7, 787],
19 [6, 5, 983], [6, 7, 214],
20 [7, 4, 1260], [7, 5, 787], [7, 6, 214], [7, 8, 888],
21 [8, 4, 864], [8, 7, 888], [8, 9, 661],
22 [8, 10, 781], [8, 11, 810],
23 [9, 8, 661], [9, 11, 1187],
24 [10, 2, 1435], [10, 4, 496], [10, 8, 781], [10, 11, 239],
25 [11, 8, 810], [11, 9, 1187], [11, 10, 239]
26 ]
236
© Copyright 2012 by Pearson Education, Inc. All Rights Reserved.
27
28 # Create a graph
29 graph1 = WeightedGraph(vertices, edges)
30
31 # Obtain a shortest path
32 tree1 = graph1.getShortestPath(5)
33 tree1.printAllPaths()
34
35 # Display shortest paths from Houston to Chicago
36 print("Shortest path from Houston to Chicago: ")
37 path = tree1.getPath(11)
38 print(path)
39
40 # Create vertices and edges
41 vertices = [x for x in range(5)]
42 edges = [
43 [0, 1, 2], [0, 3, 8],
44 [1, 0, 2], [1, 2, 7], [1, 3, 3],
45 [2, 1, 7], [2, 3, 4], [2, 4, 5],
46 [3, 0, 8], [3, 1, 3], [3, 2, 4], [3, 4, 6],
47 [4, 2, 5], [4, 3, 6]
48 ]
49
50 # Create a graph
51 graph2 = WeightedGraph(vertices, edges)
52
53 # Obtain a shortest path
54 tree2 = graph2.getShortestPath(3)
55 tree2.printAllPaths()

<Output>
All shortest paths from Chicago are:
A path from Chicago to Seattle: Chicago Seattle (cost: 2097)
A path from Chicago to San Francisco:
Chicago Denver San Francisco (cost: 2270)
A path from Chicago to Los Angeles:
Chicago Denver Los Angeles (cost: 2018)
A path from Chicago to Denver: Chicago Denver (cost: 1003)
A path from Chicago to Kansas City: Chicago Kansas City (cost: 533)
A path from Chicago to Chicago: Chicago (cost: 0)
A path from Chicago to Boston: Chicago Boston (cost: 983)
A path from Chicago to New York: Chicago New York (cost: 787)
A path from Chicago to Atlanta:
Chicago Kansas City Atlanta (cost: 1397)
A path from Chicago to Miami:
Chicago Kansas City Atlanta Miami (cost: 2058)
A path from Chicago to Dallas:
Chicago Kansas City Dallas (cost: 1029)
A path from Chicago to Houston:
Chicago Kansas City Dallas Houston (cost: 1268)

Shortest path from Chicago to Houston:


Chicago Kansas City Dallas Houston

All shortest paths from 3 are:


A path from 3 to 0: 3 1 0 (cost: 5)
A path from 3 to 1: 3 1 (cost: 3)
A path from 3 to 2: 3 2 (cost: 4)
A path from 3 to 3: 3 (cost: 0)
237
© Copyright 2012 by Pearson Education, Inc. All Rights Reserved.
A path from 3 to 4: 3 4 (cost: 6)
<End Output>
The program creates a weighted graph for Figure 23.1 in line 29. It
then invokes the getShortestPath(5) method to return a Path object that
contains all shortest paths from vertex 5 (i.e., Chicago). Invoking
printPath() on the ShortestPathTree object displays all the paths.

The graphical illustration of all shortest paths from Chicago is shown


in Figure 23.19. The shortest paths from Chicago to the cities are
found in this order: Kansas City, New York, Boston, Denver, Dallas,
Houston, Atlanta, Los Angeles, Miami, Seattle, and San Francisco.

Seattle
10

2097 Boston
3
983
Chicago
1331 214
807 2
1003 New York
787
Denver
533
1267 4 1260
11
599
1 888
San Francisco 1015
Kansas City
381 1663
8 864

7
496
Los Angeles 1435 Atlanta
5 781
810
Dallas
6 661
239

Houston 1187
9
Miami

Figure 23.19
The shortest paths from Chicago to all other cities are highlighted.

23.6 Case Study: The Weighted Nine Tail Problem

<key point>
The weighted Nine Tail problem can be reduced to the weighted
shortest path problem.
<end key point>
Section 16.9 presented the nine tail problem and solved it using the
BFS algorithm. This section presents a variation of the problem and
solves it using the shortest-path algorithm.

The nine tail problem is to find the minimum number of the moves that
lead to all coins face down. Each move flips a head coin and its
neighbors. The weighted nine tail problem assigns the number of flips
as a weight on each move. For example, you can move from the coins in
Figure 23.20a to those in Figure 23.20b by flipping the first coin in
the first row and its two neighbors. So the weight for this move is 3.

H H H T T H
T T T H T T
H H H H H H

(a) (b)

Figure 23.20
238
© Copyright 2012 by Pearson Education, Inc. All Rights Reserved.
The weight for each move is the number of flips for the move.

The weighted nine tail problem is to find the minimum number of flips
that lead to all coins face down. The problem can be reduced to finding
the shortest path from a starting node to the target node in an edge-
weighted graph. The graph has 512 nodes. Create an edge from node v to
u if there is a move from node u to node v. Assign the number of flips
to be the weight of the edge.

Recall that in §16.9 we defined a class NineTailModel for modeling the


nine tail problem. We now define a new class named
WeightedNineTailModel that extends NineTailModel, as shown in Figure
23.21. The class contains methods and also functions. The functions are
underlined. Recalled that the methods are invoked from objects, but
functions are invoked standalone.

NineTailModel
tree: Tree A tree rooted at node 511.
NineTailModel() Constructs a model for the nine tail problem and obtains th e
tree.
getShortestPath(nodeIndex: int): list Returns a path from the specified node to the root. The path
returned consists of the node labels in a list.
getEdges (): list Returns an ed ge lis t for the graph.
getNode(index: int): list Returns a node consisting of nine characters of H’s and T’s.
getIndex(node: list): int Returns the index of the specified node.
getFlippedNode(n ode: list, position: Flips the node at the specified position and returns the index
int): int of the flipped node.
flip ACell(node: list, row: in t, colu mn: Flips the node at the specified row and column.
int): void
printNode(n od e: list): void Dis plays the node to the console.

WeightedNineTailModel
WeightedNineTailM od el() Constructs a m odel for the weighted nine tail problem
and obtains a ShortestPathTree rooted from the target
node.
getNumberOfFlipsFrom(u: int): int Return s the number of flips from node u to the target
node 511.
getNumberOfFlips(u: int, v: int): int Return s the number of different cells between the two
nodes.
getWeightedEdges(): list Creates and return all edges for the graph.

Figure 23.21
WeightedNineTailModel extends NineTailModel.

The NineTailModel class creates a Graph and obtains a Tree rooted at


the target node 511. WeightedNineTailModel is the same as NineTailModel
except that it creates a WeightedGraph and obtains a ShortestPathTree
rooted at the target node 511. WeightedNineTailModel extends
NineTailModel. The method getWeightedEdges() finds all weighted edges
in the graph. The getNumberOfFlips(int u, int v) method returns the
number of flips from node u to node v. The getNumberOfFlipsFrom(int u)
method returns the number of flips from node u to the target node.

Listing 23.8 implements WeightedNineTailModel.

239
© Copyright 2012 by Pearson Education, Inc. All Rights Reserved.
Listing 23.8 WeightedNineTailModel.py
<Side Remark line 10: extends NineTailModel>
<Side Remark line 12: constructor>
<Side Remark line 16: create vertices>
<Side Remark line 17: create a graph>
<Side Remark line 20: get a tree>
<Side Remark line 22: number of flips at u>
<Side Remark line 23: total number of flips>
<Side Remark line 26: get weighted edges>
<Side Remark line 34: get adjacent node>
<Side Remark line 35: weight>
<Side Remark line 38: add an edge>
<Side Remark line 38: flips from u to v>
1 from WeightedGraph import WeightedGraph
2 from WeightedGraph import WeightedEdge
3 from NineTailModel import NineTailModel
4 from NineTailModel import NUMBER_OF_NODES
5 from NineTailModel import getIndex
6 from NineTailModel import getNode
7 from NineTailModel import printNode
8 from NineTailModel import getFlippedNode
9
10 class WeightedNineTailModel(NineTailModel):
11 # Construct a model
12 def __init__(self):
13 NineTailModel.__init__(self) # Invoke superclass constructor
14
15 # Create a graph
16 vertices = [x for x in range(NUMBER_OF_NODES)]
17 graph = WeightedGraph(vertices, getWeightedEdges());
18
19 # Obtain a BSF tree rooted at the target node
20 self.tree = graph.getShortestPath(511)
21
22 def getNumberOfFlipsFrom(self, u):
23 return self.tree.getCost(u)
24
25 # Create all edges for the graph
26 def getWeightedEdges():
27 # Store edges
28 edges = []
29
30 for u in range(NUMBER_OF_NODES):
31 for k in range(9):
32 node = getNode(u) # Get the node for vertex u
33 if node[k] == 'H':
34 v = getFlippedNode(node, k)
35 numberOfFlips = getNumberOfFlips(u, v)
36
37 # Add edge (v, u) for a legal move from node u to node v
38 edges.append([v, u, numberOfFlips])
39
40 return edges
41
42 def getNumberOfFlips(u, v):
43 node1 = getNode(u)
44 node2 = getNode(v)
45
46 count = 0 # Count the number of different cells
240
© Copyright 2012 by Pearson Education, Inc. All Rights Reserved.
47 for i in range(len(node1)):
48 if node1[i] != node2[i]:
49 count += 1
50
51 return count

WeightedNineTailModel extends NineTailModel to build a WeightedGraph to


model the weighted nine tail problem (lines 10–11). For each node u,
the getWeightedEdges() method finds a flipped node v and assigns the
number of flips as the weight for edge (u, v) (line 34). The
getNumberOfFlips(int u, int v) method returns the number of flips from
node u to node v (lines 42–51). The number of flips is the number of
the different cells between the two nodes (line 35).

The WeightedNineTailModel constructs a WeightedGraph (lines 16–17) and


obtains a ShortestPathTree rooted at the target node 511 (line 20).
Note that tree is a data field defined NineTailModel and
ShortestPathTree is a subclass of Tree. The methods defined in
NineTailModel use the tree property.

The getNumberOfFlipsFrom(int u) method (lines 22–23) returns the number


of flips from node u to the target node, which is the cost of the path
from node u to the target node. This cost can be obtained by invoking
the getCost(u) method defined in the ShortestPathTree class (line 23).

Listing 23.9 gives a program that prompts the user to enter an initial
node and displays the minimum number of flips to reach the target node.

Listing 23.9 WeightedNineTail.py


<Side Remark line 8: initial node>
<Side Remark line 11: create model>
<Side Remark line 12: get shortest path>
<Side Remark line 16: print node>
<Side Remark line 19: number of flips>
1 from WeightedNineTailModel import WeightedNineTailModel
2 from NineTailModel import getIndex
3 from NineTailModel import getNode
4 from NineTailModel import printNode
5
6 def main():
7 # Prompt the user to enter nine coins H's and T's
8 initialNode = \
9 input("Enter an initial nine coin H's and T's: ").strip()
10
11 # Create the NineTaileModel
12 model = WeightedNineTailModel()
13 path = model.getShortestPath(getIndex(initialNode))
14
15 print("The steps to flip the coins are ")
16 for i in range(len(path)):
17 printNode(getNode(path[i]))
18
19 print("The number of flips is " +
20 str(model.getNumberOfFlipsFrom(getIndex(initialNode))))
21
22 main()

<Output>
241
© Copyright 2012 by Pearson Education, Inc. All Rights Reserved.
Enter an initial nine coin H’s and T's: HHHTTTHHH

The steps to flip the coins are


HHH
TTT
HHH

HHH
THT
TTT

TTT
TTT
TTT

The number of flips is 8


<End Output>
The program prompts the user to enter an initial node with nine
letters, H’s and T’s, as a string in line 8, creates a model (line 11),
obtains a shortest path from the initial node to the target node (line
12), displays the nodes in the path (lines 15–16), and invokes
getNumberOfFlipsFrom to get the number of flips needed to reach to the
target node (line 19).

<check point>
23.5
Trace Dijkstra’s algorithm for finding shortest paths from Boston to
all other cities in Figure 23.1.

23.6
Is the shortest path between two vertices unique if all edges have
different weights?

23.7
If you use an adjacency matrix to represent weighted edges, what would
be the time complexity for Dijkstra’s algorithm?

23.8
What happens to the getShortestPath() method in WeightedGraph if the
graph is not connected? Verify your answer by writing a test program
that creates an unconnected graph and invoke the getShortestPath()
method.
<end check point>
Key Terms

 Dijkstra’s algorithm
 edge-weighted graph
 minimum spanning tree
 Prim’s algorithm
 shortest path
 single-source shortest path
 vertex-weighted graph

Chapter Summary
1. You can use adjacency matrices or priority queues to store
weighted edges.
242
© Copyright 2012 by Pearson Education, Inc. All Rights Reserved.
2. A spanning tree of a graph is a subgraph that is a tree and
connects all vertices in the graph. You learned how to implement
Prim’s algorithm for finding a minimum spanning tree.
3. You learned how to implement Dijkstra’s algorithm for finding
shortest paths.

Multiple-Choice Questions

See multiple-choice questions online at


liang.armstrong.edu/selftest/selftestpyhapter=23.

Programming Exercises

23.1*
(Kruskal’s algorithm) The text introduced Prim’s algorithm for finding
a minimum spanning tree. Kruskal’s algorithm is another
well-known algorithm for finding a minimum spanning tree.
The algorithm repeatedly finds a minimum-weight edge and
adds it to the tree if it does not cause a cycle. The
process ends when all vertices are in the tree. Design and
implement an algorithm for finding an MST using Kruskal’s
algorithm.

23.2*
(Implementing Prim’s algorithm using adjacency matrix) The text
implements Prim’s algorithm using priority queues on
adjacent edges. Implement the algorithm using adjacency
matrix for weighted graphs.

23.3*
(Implementing Dijkstra’s algorithm using adjacency matrix) The text
implements Dijkstra’s algorithm using priority queues on
adjacent edges. Implement the algorithm using adjacency
matrix for weighted graphs.

23.4*
(Modifying weight in the nine tail problem) In the text, we assign the
number of the flips as the weight for each move. Assuming that the
weight is three times of the number of flips, revise the program.

23.5*
(Prove or disprove) The conjecture is that both NineTailModel and
WeightedNineTailModel result in the same shortest path. Write a program
to prove or disprove it. (Hint: Let tree1 and tree2 denote the trees
rooted at node 511 obtained from NineTailModel and
WeightedNineTailModel, respectively. If the depth of a node u is the
same in tree1 and in tree2, the length of the path from u to the target
is the same.)

23.6**
(Weighted 4 × 4 16 tail model) The weighted nine tail problem in the
text uses a 3 × 3 matrix. Assume that you have 16 coins placed in a 4 ×
4 matrix. Create a new model class named WeightedTailModel16. Create an
instance of the model and save the object into a file named
Exercise23_6.dat.

23.7**

243
© Copyright 2012 by Pearson Education, Inc. All Rights Reserved.
(Weighted 4 × 4 16 tail view) Listing 23.12, NineTailApp.py, presents a
view for the nine tail problem. Revise this program for the weighted 4
× 4 16 tail problem. Your program should read the model object created
from the preceding exercise.

23.8**
(Traveling salesman problem) The traveling salesman problem (TSP) is to
find a shortest round-trip route that visits each city exactly once and
then returns to the starting city. The problem is equivalent to finding
a shortest Hamiltonian cycle. Add the following method in the
WeightedGraph class:

# Return a shortest cycle


# Return null if no such cycle exists
def getShortestHamiltonianCycle():

23.9*
(Finding a minimum spanning tree) Write a program that reads a
connected graph from a file and displays its minimum spanning tree. The
first line in the file contains a number that indicates the number of
vertices (n). The vertices are labeled as 0, 1, ..., n-1. Each
subsequent line describes the edges in the form of u1, v1, w1 | u2, v2,
w2 | .... Each triplet in this form describes an edge and its weight.
Figure 23.22 shows an example of the file for the corresponding graph.
Note that we assume the graph is undirected. If the graph has an edge
(u, v), it also has an edge (v, u). Only one edge is represented in the
file. When you construct a graph, both edges need to be considered.
100 1
0
File
3 20
6
0, 1, 100 | 0, 2, 3
40
2 3 1, 3, 20
2, 3, 40 | 2, 4, 2
5
2 5 3, 4, 5 | 3, 5, 5
9 4, 5, 9
4 5
(a) (b)

Figure 23.22
The vertices and edges of a weighted graph can be stored in a
file.

Your program should prompt the user to enter the name of the file,
should read data from a file, create an instance g of WeightedGraph,
invoke g.printWeightedEdges() to display all edges, invoke
getMinimumSpanningTree() to obtain an instance tree of
WeightedGraph.MST, invoke tree.getTotalWeight() to display the weight
of the minimum spanning tree, and invoke tree.printTree() to display
the tree. Here is a sample run of the program:

<Output>
Enter a file name: c:\exercise\Exercise23_9.txt
The number of vertices is 6
Vertex 0: (0, 2, 3) (0, 1, 100)
Vertex 1: (1, 3, 20) (1, 0, 100)
Vertex 2: (2, 4, 2) (2, 3, 40) (2, 0, 3)
Vertex 3: (3, 4, 5) (3, 5, 5) (3, 1, 20) (3, 2, 40)
Vertex 4: (4, 2, 2) (4, 3, 5) (4, 5, 9)
Vertex 5: (5, 3, 5) (5, 4, 9)
Total weight is 35
244
© Copyright 2012 by Pearson Education, Inc. All Rights Reserved.
Root is: 0
Edges: (3, 1) (0, 2) (4, 3) (2, 4) (3, 5)
<End Output>
(Hint: Use new WeightedGraph(list, numberOfVertices) to create a graph,
where list contains a list of WeightedEdge objects. Use new
WeightedEdge(u, v, w) to create an edge. Read the first line to get the
number of vertices. Read each subsequent line into a string s and use
s.split("[\\|]") to extract the triplets. For each triplet,
triplet.split("[,]") to extract vertices and weight.)

23.10*
(Creating a file for a graph) Modify Listing 23.3,
TestWeightedGraph.py, to create a file for representing graph1. The
file format is described in Exercise 23.9. Create the file from the
array defined in lines 7-24 in Listing 23.3. The number of vertices for
the graph is 12, which will be stored in the first line of the file. An
edge (u, v) is stored if u < v. The contents of the file should be as
follows:

12
0, 1, 807 | 0, 3, 1331 | 0, 5, 2097
1, 2, 381 | 1, 3, 1267
2, 3, 1015 | 2, 4, 1663 | 2, 10, 1435
3, 4, 599 | 3, 5, 1003
4, 5, 533 | 4, 7, 1260 | 4, 8, 864 | 4, 10, 496
5, 6, 983 | 5, 7, 787
6, 7, 214
7, 8, 888
8, 9, 661 | 8, 10, 781 | 8, 11, 810
9, 11, 1187
10, 11, 239

23.11*
(Finding shortest paths) Write a program that reads a connected graph
from a file. The graph is stored in a file using the same format
specified in Exercise 23.9. Your program should prompt the user to
enter the name of the file, then two vertices, and should display the
shortest path between the two vertices. For example, for the graph in
Figure 23.21, a shortest path between 0 and 1 may be displayed as 0 2 4
3 1.

Here is a sample run of the program:

<Output>
Enter a file name: Exercise23_11.txt
Enter two vertices (integer indexes): 0 1
The number of vertices is 6
Vertex 0: (0, 2, 3) (0, 1, 100)
Vertex 1: (1, 3, 20) (1, 0, 100)
Vertex 2: (2, 4, 2) (2, 3, 40) (2, 0, 3)
Vertex 3: (3, 4, 5) (3, 5, 5) (3, 1, 20) (3, 2, 40)
Vertex 4: (4, 2, 2) (4, 3, 5) (4, 5, 9)
Vertex 5: (5, 3, 5) (5, 4, 9)
A path from 0 to 1: 0 2 4 3 1
<End Output>
23.12*

245
© Copyright 2012 by Pearson Education, Inc. All Rights Reserved.
(Displaying weighted graphs) Revise GraphView in Listing 23.6 to
display a weighted graph. Write a program that displays the graph in
Figure 23.1 as shown in Figure 23.23a.

(a) (b)
Figure 23.23
(a) Exercise 23.12 displays a weighted graph. (b) Exercise 23.14
displays an MST.

23.13*
(Displaying shortest paths) Revise GraphView in Listing 23.6 to display
a weighted graph and a shortest path between the two specified cities,
as shown in Figure 23.18. You need to add a data field path in
GraphView. If a path is set, the edges in the path are displayed in
red. If a city not in the map is entered, the program displays a dialog
box to alert the user.

23.14*
(Displaying a minimum spanning tree) Revise GraphView in Listing 23.6
to display a weighted graph and a minimum spanning tree for the graph
in Figure 23.1, as shown in Figure 23.23b. The edges in the MST are
shown in red.

23.15***
(Weighted graph visualization tool) Develop a GUI program as shown in
Figure 23.2, with the following requirements: (1) The radius of each
vertex is 20 pixels. (2) The user clicks the left-mouse button to place
a vertex centered at the mouse point provided that the mouse point is
not inside or too close to an existing vertex. (3) The user clicks the
right-mouse button inside an existing vertex to remove the vertex. (4)
The user presses a mouse button inside a vertex and drags to another
vertex and then releases the button to create an edge and distance
between the two vertices is also displayed. (5) The user drags a vertex
while pressing the CTRL key to move a vertex. (6) The vertices are
numbers starting from 0. When a vertex is removed, the vertices are
renumbered. (7) You can click the Show MST or Show All SP From the
Source button to display a MST or SP tree from a starting vertex. (8)
You can click the Show Shortest Path button to display the shortest
path between the two specified vertices.

246
© Copyright 2012 by Pearson Education, Inc. All Rights Reserved.

You might also like