Why must depth-first search explore every cycle-free path before it can claim the shortest one, and how does it avoid looping forever?
Short drills on what this video just taught. Write the code, run the checks, and reveal the answer only if you are stuck.
To avoid going round a cycle, depth-first search never steps to a node that is already on the current path. Define nextSteps(graph, node, path) that returns the names of the children of node that are not on path (a list of nodes), in the order childrenOf lists them. The city graph from the video is built for you by buildCityGraph.
class Node(object):
def __init__(self, name):
"""Assumes name is a string"""
self.name = name
def getName(self):
return self.name
def __str__(self):
return self.name
class Edge(object):
def __init__(self, src, dest):
"""Assumes src and dest are nodes"""
self.src = src
self.dest = dest
def getSource(self):
return self.src
def getDestination(self):
return self.dest
def __str__(self):
return self.src.getName() + '->' + self.dest.getName()
class Digraph(object):
"""edges is a dict mapping each node to a list of its children"""
def __init__(self):
self.edges = {}
def addNode(self, node):
if node in self.edges:
raise ValueError('Duplicate node')
else:
self.edges[node] = []
def addEdge(self, edge):
src = edge.getSource()
dest = edge.getDestination()
if not (src in self.edges and dest in self.edges):
raise ValueError('Node not in graph')
self.edges[src].append(dest)
def childrenOf(self, node):
return self.edges[node]
def hasNode(self, node):
return node in self.edges
def getNode(self, name):
for n in self.edges:
if n.getName() == name:
return n
raise NameError(name)
def __str__(self):
result = ''
for src in self.edges:
for dest in self.edges[src]:
result = result + src.getName() + '->' + dest.getName() + '\n'
return result[:-1]
class Graph(Digraph):
def addEdge(self, edge):
Digraph.addEdge(self, edge)
rev = Edge(edge.getDestination(), edge.getSource())
Digraph.addEdge(self, rev)
def buildCityGraph(graphType):
g = graphType()
for name in ('Boston', 'Providence', 'New York', 'Chicago',
'Denver', 'Phoenix', 'Los Angeles'):
g.addNode(Node(name))
g.addEdge(Edge(g.getNode('Boston'), g.getNode('Providence')))
g.addEdge(Edge(g.getNode('Boston'), g.getNode('New York')))
g.addEdge(Edge(g.getNode('Providence'), g.getNode('Boston')))
g.addEdge(Edge(g.getNode('Providence'), g.getNode('New York')))
g.addEdge(Edge(g.getNode('New York'), g.getNode('Chicago')))
g.addEdge(Edge(g.getNode('Chicago'), g.getNode('Denver')))
g.addEdge(Edge(g.getNode('Chicago'), g.getNode('Phoenix')))
g.addEdge(Edge(g.getNode('Denver'), g.getNode('Phoenix')))
g.addEdge(Edge(g.getNode('Denver'), g.getNode('New York')))
g.addEdge(Edge(g.getNode('Los Angeles'), g.getNode('Boston')))
return g
def printPath(path):
"""Assumes path is a list of nodes"""
result = ''
for i in range(len(path)):
result = result + str(path[i])
if i != len(path) - 1:
result = result + '->'
return result