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