1
+ class Node (object ):
2
+ def __init__ (self , value ):
3
+ self .value = value
4
+ self .edges = []
5
+ self .visited = False
6
+
7
+ class Edge (object ):
8
+ def __init__ (self , value , node_from , node_to ):
9
+ self .value = value
10
+ self .node_from = node_from
11
+ self .node_to = node_to
12
+
13
+ # You only need to change code with docs strings that have TODO.
14
+ # Specifically: Graph.dfs_helper and Graph.bfs
15
+ # New methods have been added to associate node numbers with names
16
+ # Specifically: Graph.set_node_names
17
+ # and the methods ending in "_names" which will print names instead
18
+ # of node numbers
19
+
20
+ class Graph (object ):
21
+ def __init__ (self , nodes = None , edges = None ):
22
+ self .nodes = nodes or []
23
+ self .edges = edges or []
24
+ self .node_names = []
25
+ self ._node_map = {}
26
+
27
+ def set_node_names (self , names ):
28
+ """The Nth name in names should correspond to node number N.
29
+ Node numbers are 0 based (starting at 0).
30
+ """
31
+ self .node_names = list (names )
32
+
33
+ def insert_node (self , new_node_val ):
34
+ "Insert a new node with value new_node_val"
35
+ new_node = Node (new_node_val )
36
+ self .nodes .append (new_node )
37
+ self ._node_map [new_node_val ] = new_node
38
+ return new_node
39
+
40
+ def insert_edge (self , new_edge_val , node_from_val , node_to_val ):
41
+ "Insert a new edge, creating new nodes if necessary"
42
+ nodes = {node_from_val : None , node_to_val : None }
43
+ for node in self .nodes :
44
+ if node .value in nodes :
45
+ nodes [node .value ] = node
46
+ if all (nodes .values ()):
47
+ break
48
+ for node_val in nodes :
49
+ nodes [node_val ] = nodes [node_val ] or self .insert_node (node_val )
50
+ node_from = nodes [node_from_val ]
51
+ node_to = nodes [node_to_val ]
52
+ new_edge = Edge (new_edge_val , node_from , node_to )
53
+ node_from .edges .append (new_edge )
54
+ node_to .edges .append (new_edge )
55
+ self .edges .append (new_edge )
56
+
57
+ def get_edge_list (self ):
58
+ """Return a list of triples that looks like this:
59
+ (Edge Value, From Node, To Node)"""
60
+ return [(e .value , e .node_from .value , e .node_to .value )
61
+ for e in self .edges ]
62
+
63
+ def get_edge_list_names (self ):
64
+ """Return a list of triples that looks like this:
65
+ (Edge Value, From Node Name, To Node Name)"""
66
+ return [(edge .value ,
67
+ self .node_names [edge .node_from .value ],
68
+ self .node_names [edge .node_to .value ])
69
+ for edge in self .edges ]
70
+
71
+ def get_adjacency_list (self ):
72
+ """Return a list of lists.
73
+ The indecies of the outer list represent "from" nodes.
74
+ Each section in the list will store a list
75
+ of tuples that looks like this:
76
+ (To Node, Edge Value)"""
77
+ max_index = self .find_max_index ()
78
+ adjacency_list = [[] for _ in range (max_index )]
79
+ for edg in self .edges :
80
+ from_value , to_value = edg .node_from .value , edg .node_to .value
81
+ adjacency_list [from_value ].append ((to_value , edg .value ))
82
+ return [a or None for a in adjacency_list ] # replace []'s with None
83
+
84
+ def get_adjacency_list_names (self ):
85
+ """Each section in the list will store a list
86
+ of tuples that looks like this:
87
+ (To Node Name, Edge Value).
88
+ Node names should come from the names set
89
+ with set_node_names."""
90
+ adjacency_list = self .get_adjacency_list ()
91
+ def convert_to_names (pair , graph = self ):
92
+ node_number , value = pair
93
+ return (graph .node_names [node_number ], value )
94
+ def map_conversion (adjacency_list_for_node ):
95
+ if adjacency_list_for_node is None :
96
+ return None
97
+ return map (convert_to_names , adjacency_list_for_node )
98
+ return [map_conversion (adjacency_list_for_node )
99
+ for adjacency_list_for_node in adjacency_list ]
100
+
101
+ def get_adjacency_matrix (self ):
102
+ """Return a matrix, or 2D list.
103
+ Row numbers represent from nodes,
104
+ column numbers represent to nodes.
105
+ Store the edge values in each spot,
106
+ and a 0 if no edge exists."""
107
+ max_index = self .find_max_index ()
108
+ adjacency_matrix = [[0 ] * (max_index ) for _ in range (max_index )]
109
+ for edg in self .edges :
110
+ from_index , to_index = edg .node_from .value , edg .node_to .value
111
+ adjacency_matrix [from_index ][to_index ] = edg .value
112
+ return adjacency_matrix
113
+
114
+ def find_max_index (self ):
115
+ """Return the highest found node number
116
+ Or the length of the node names if set with set_node_names()."""
117
+ if len (self .node_names ) > 0 :
118
+ return len (self .node_names )
119
+ max_index = - 1
120
+ if len (self .nodes ):
121
+ for node in self .nodes :
122
+ if node .value > max_index :
123
+ max_index = node .value
124
+ return max_index
125
+
126
+ def find_node (self , node_number ):
127
+ "Return the node with value node_number or None"
128
+ return self ._node_map .get (node_number )
129
+
130
+ def _clear_visited (self ):
131
+ for node in self .nodes :
132
+ node .visited = False
133
+
134
+ def dfs_helper (self , start_node ):
135
+ """TODO: Write the helper function for a recursive implementation
136
+ of Depth First Search iterating through a node's edges. The
137
+ output should be a list of numbers corresponding to the
138
+ values of the traversed nodes.
139
+ ARGUMENTS: start_node is the starting Node
140
+ MODIFIES: the value of the visited property of nodes in self.nodes
141
+ RETURN: a list of the traversed node values (integers).
142
+ """
143
+ ret_list = [start_node .value ]
144
+ # Your code here
145
+ start_node .visited = True # set current node to visited
146
+ for edge in start_node .edges : # for edge in the list of edges
147
+ if edge .node_to .visited == False : # if it hasn't been visited
148
+ ret_list = ret_list + self .dfs_helper (edge .node_to ) # run this fn on that node, and update list with returned list
149
+ return ret_list
150
+
151
+ def dfs (self , start_node_num ):
152
+ """Outputs a list of numbers corresponding to the traversed nodes
153
+ in a Depth First Search.
154
+ ARGUMENTS: start_node_num is the starting node number (integer)
155
+ MODIFIES: the value of the visited property of nodes in self.nodes
156
+ RETURN: a list of the node values (integers)."""
157
+ self ._clear_visited ()
158
+ start_node = self .find_node (start_node_num )
159
+ return self .dfs_helper (start_node )
160
+
161
+ def dfs_names (self , start_node_num ):
162
+ """Return the results of dfs with numbers converted to names."""
163
+ return [self .node_names [num ] for num in self .dfs (start_node_num )]
164
+
165
+ def bfs (self , start_node_num ):
166
+ """TODO: Create an iterative implementation of Breadth First Search
167
+ iterating through a node's edges. The output should be a list of
168
+ numbers corresponding to the traversed nodes.
169
+ ARGUMENTS: start_node_num is the node number (integer)
170
+ MODIFIES: the value of the visited property of nodes in self.nodes
171
+ RETURN: a list of the node values (integers)."""
172
+ node = self .find_node (start_node_num )
173
+ self ._clear_visited ()
174
+ ret_list = []
175
+ queue = [node ]
176
+ # Your code here
177
+ while queue :
178
+ curr = queue .pop (0 )
179
+ if curr .visited == False :
180
+ ret_list .append (curr .value )
181
+ for edge in curr .edges :
182
+ node_to = edge .node_to
183
+ if node_to .visited == False :
184
+ queue .append (node_to )
185
+ curr .visited = True
186
+ return ret_list
187
+
188
+ def bfs_names (self , start_node_num ):
189
+ """Return the results of bfs with numbers converted to names."""
190
+ return [self .node_names [num ] for num in self .bfs (start_node_num )]
191
+
192
+ graph = Graph ()
193
+
194
+ # You do not need to change anything below this line.
195
+ # You only need to implement Graph.dfs_helper and Graph.bfs
196
+
197
+ graph .set_node_names (('Mountain View' , # 0
198
+ 'San Francisco' , # 1
199
+ 'London' , # 2
200
+ 'Shanghai' , # 3
201
+ 'Berlin' , # 4
202
+ 'Sao Paolo' , # 5
203
+ 'Bangalore' )) # 6
204
+
205
+ graph .insert_edge (51 , 0 , 1 ) # MV <-> SF
206
+ graph .insert_edge (51 , 1 , 0 ) # SF <-> MV
207
+ graph .insert_edge (9950 , 0 , 3 ) # MV <-> Shanghai
208
+ graph .insert_edge (9950 , 3 , 0 ) # Shanghai <-> MV
209
+ graph .insert_edge (10375 , 0 , 5 ) # MV <-> Sao Paolo
210
+ graph .insert_edge (10375 , 5 , 0 ) # Sao Paolo <-> MV
211
+ graph .insert_edge (9900 , 1 , 3 ) # SF <-> Shanghai
212
+ graph .insert_edge (9900 , 3 , 1 ) # Shanghai <-> SF
213
+ graph .insert_edge (9130 , 1 , 4 ) # SF <-> Berlin
214
+ graph .insert_edge (9130 , 4 , 1 ) # Berlin <-> SF
215
+ graph .insert_edge (9217 , 2 , 3 ) # London <-> Shanghai
216
+ graph .insert_edge (9217 , 3 , 2 ) # Shanghai <-> London
217
+ graph .insert_edge (932 , 2 , 4 ) # London <-> Berlin
218
+ graph .insert_edge (932 , 4 , 2 ) # Berlin <-> London
219
+ graph .insert_edge (9471 , 2 , 5 ) # London <-> Sao Paolo
220
+ graph .insert_edge (9471 , 5 , 2 ) # Sao Paolo <-> London
221
+ # (6) 'Bangalore' is intentionally disconnected (no edges)
222
+ # for this problem and should produce None in the
223
+ # Adjacency List, etc.
224
+
225
+ import pprint
226
+ pp = pprint .PrettyPrinter (indent = 2 )
227
+
228
+ print "Edge List"
229
+ pp .pprint (graph .get_edge_list_names ())
230
+
231
+ print "\n Adjacency List"
232
+ pp .pprint (graph .get_adjacency_list_names ())
233
+
234
+ print "\n Adjacency Matrix"
235
+ pp .pprint (graph .get_adjacency_matrix ())
236
+
237
+ print "\n Depth First Search"
238
+ pp .pprint (graph .dfs_names (2 ))
239
+
240
+ # Should print:
241
+ # Depth First Search
242
+ # ['London', 'Shanghai', 'Mountain View', 'San Francisco', 'Berlin', 'Sao Paolo']
243
+
244
+ print "\n Breadth First Search"
245
+ pp .pprint (graph .bfs_names (2 ))
246
+ # test error reporting
247
+ # pp.pprint(['Sao Paolo', 'Mountain View', 'San Francisco', 'London', 'Shanghai', 'Berlin'])
248
+
249
+ # Should print:
250
+ # Breadth First Search
251
+ # ['London', 'Shanghai', 'Berlin', 'Sao Paolo', 'Mountain View', 'San Francisco']
0 commit comments