Files in the same folder ------------------------ Depending on which implementat
ID: 3756546 • Letter: F
Question
Files in the same folder ------------------------ Depending on which implementation you use for part 1, you may need to include other files in the same folder.
Imports ------- Depending on which implementation you use for part 1, you may need to include additional import statement(s).
Part 1 ------ This week's example files include 2 different implementations of this function. Choose one and copy that implementation here.
Part 2 ------ The courses and their prerequisites are shown in a comment. The courses are each named with a single capital letter.
Part 3 ------ The courses and their prerequisites are shown in a comment.
Part 4 ------ Use an appropriate graph iterator.
Part 5 ------ Use an appropriate graph iterator.
Part 6 ------ If you used the stack-based implementation of the topologicalSort() function, it can produce a different, correct ordering for each run of your program.
The following are some of the possible correct orderings: B A C E D F G H A C E B D F G H A B C E D F G H A B C D F E G H B A C D F E G H
~~~~~~~~~~~~~~~~~~
# This program exercises graphs.
# Replace any "<your code>" comments with your own code statement(s) # to accomplish the specified task. # Do not change any other code.
# The following files must be in the same folder: # abstractcollection.py # graph.py
from graph import LinkedDirectedGraph
# Part 1: # Complete the following function: def topologicalSort(graph): # <your code>
graph = LinkedDirectedGraph()
# The graph represents the following course prerequisites: # A requires nothing # B requires nothing # C requires A # D requires A, B, and C # E requires C # F requires B and D # G requires E and F # H requires C, F, and G
# Part 2: # Add the vertices: # <your code>
# Part 3: # Add the edges: # <your code>
print("Graph:") print(graph) print()
print("Courses:") # Part 4: # Display each vertex on a separate line: # <your code> print()
print("Prerequisites:") # Part 5: # Display each edge on a separate line: # <your code> print()
print("One possible order to take the courses:") # Part 6: # Display the courses in prerequisite (topological) order: # <your code> print()
~~~~~~~~~~~~~~~
""" File: abstractcollection.py Copyright 2015 by Ken Lambert
"""
class AbstractCollection(object): """An abstract collection implementation."""
# Constructor def __init__(self, sourceCollection = None): """Sets the initial state of self, which includes the contents of sourceCollection, if it's present.""" self._size = 0 self._modCount = 0 if sourceCollection: for item in sourceCollection: self.add(item)
# Accessor methods def isEmpty(self): """Returns True if len(self) == 0, or False otherwise.""" return len(self) == 0 def __len__(self): """Returns the number of items in self.""" return self._size
def __str__(self): """Returns the string representation of self, using [] as delimiters.""" return "[" + ", ".join(map(str, self)) + "]"
def __eq__(self, other): """Returns True if self equals other, or False otherwise. Compares pairs of items in the two sequences generated by the iterators on self and other.""" if self is other: return True if type(self) != type(other) or len(self) != len(other): return False otherIter = iter(other) for item in self: if item != next(otherIter): return False return True
def __add__(self, other): """Returns a new collection containing the contents of self and other.""" result = type(self)(self) for item in other: result.add(item) return result
def count(self, item): """Returns the number of instance of item in self.""" counter = 0 for nextItem in self: if item == nextItem: counter += 1 return counter
# These methods track and update the modCount, which is used to # prevent mutations within the context of an iterator (for loop)
def getModCount(self): """Returns the number of modifications to the collection.""" return self._modCount
def incModCount(self): """Increments the number of modifications to the collection.""" self._modCount += 1
~~~~~~~~~~~~~~~
""" File: graph.py Copyright 2015 by Ken Lambert
Adds the in, +, ==, and clone operations to support graph-specific behavior. """
from abstractcollection import AbstractCollection
class LinkedEdge(object): # An edge has a source vertex, a destination vertex, # a weight, and a mark attribute.
def __init__(self, fromVertex, toVertex, weight = None): self._vertex1 = fromVertex self._vertex2 = toVertex self._weight = weight self._mark = False def clearMark(self): """Clears the mark on the edge.""" self._mark = False def __eq__(self, other): """Two edges are equal if they connect the same vertices.""" if self is other: return True if type(self) != type(other): return False return self._vertex1 == other._vertex1 and self._vertex2 == other._vertex2
def getFromVertex(self): # Derrf Seitz 12/17/2015 """Returns the edge's origin vertex.""" return self._vertex1 def getOtherVertex(self, thisVertex): """Returns the vertex opposite thisVertex.""" if thisVertex == None or thisVertex == self._vertex2: return self._vertex1 else: return self._vertex2
def getToVertex(self): """Returns the edge's destination vertex.""" return self._vertex2 def getWeight(self): """Returns the edge's weight.""" return self._weight def isMarked(self): """Returns True if the edge is marked or False otherwise.""" return self._mark def setMark(self): """Sets the mark on the edge.""" self._mark = True def setWeight(self, weight): """Sets the weight on the edge to weight.""" self._weight = weight def __str__(self): """Returns the string representation of the edge.""" return str(self._vertex1) + ">" + str(self._vertex2) + ":" + str(self._weight)
class LinkedVertex(object):
# A vertex has a label, a list of incident edges, # and a mark attribute.
def __init__(self, label): self._label = label self._edgeList = list() self._mark = False
def clearMark(self): """Clears the mark on the vertex.""" self._mark = False; def getLabel(self): """Returns the label of the vertex.""" return self._label def isMarked(self): """Returns True if the vertex is marked or False otherwise.""" return self._mark def setLabel(self, label, g): """Sets the vertex's label to label.""" g._vertices.pop(self._label, None) g._vertices[label] = self self._label = label
def setMark(self): """Sets the mark on the vertex.""" self._mark = True def __str__(self): """Returns the string representation of the vertex.""" return str(self._label)
def __eq__(self, other): """Two vertices are equal if they have the same labels.""" if self is other: return True if type(self) != type(other): return False return self.getLabel() == other.getLabel() def __hash__(self): # Derrf Seitz 12/16/2015 """Hash method for a vertex.""" return hash(self._label)
# Methods used by LinkedGraph def addEdgeTo(self, toVertex, weight): """Connects the vertices with an edge.""" edge = LinkedEdge(self, toVertex, weight) self._edgeList.append(edge) def getEdgeTo(self, toVertex): """Returns the connecting edge if it exists, or None otherwise.""" edge = LinkedEdge(self, toVertex) try: return self._edgeList[self._edgeList.index(edge)] except: return None
def incidentEdges(self): """Returns the incident edges of this vertex.""" return iter(self._edgeList) def neighboringVertices(self): """Returns the neighboring vertices of this vertex.""" vertices = list() for edge in self._edgeList: vertices.append(edge.getOtherVertex(self)) return iter(vertices) def removeEdgeTo(self, toVertex): """Returns True if the edge exists and is removed, or False otherwise.""" edge = LinkedEdge(self, toVertex) if edge in self._edgeList: self._edgeList.remove(edge) return True else: return False
class LinkedDirectedGraph(AbstractCollection):
# A graph has a count of vertices, a count of edges, # and a dictionary of label/vertex pairs.
def __init__(self, sourceCollection = None): self._edgeCount = 0 self._vertices = {} AbstractCollection.__init__(self, sourceCollection) # in, ==, +, and clone operations
def __contains__(self, item): """Returns True if item is a label in a vertex, or False otherwise.""" for vertex in self: if vertex.getLabel() == item: return True return False
def clone(self): """Returns an exact copy of self, including vertex labels, edges connecting vertices, and edge weights.""" newGraph = type(self)(map(lambda vertex: vertex.getLabel(), self)) for vertex in self: for neighbor in vertex.neighboringVertices(): edge = vertex.getEdgeTo(neighbor) newGraph.addEdge(vertex.getLabel(), neighbor.getLabel(), edge.getWeight()) return newGraph
def __add__(self, other): """Returns a new collection consisting of the items in self and other. This will be a graph with the contents of the operand graphs as separate components. Precondition: the labels in the two graph operands are disjoint. Raises: AttributeError if the labels in the two operand graphs are not disjoint.""" newGraph = self.clone() for vertex in other: newGraph.add(vertex.getLabel()) for vertex in other: for neighbor in vertex.neighboringVertices(): edge = vertex.getEdgeTo(neighbor) newGraph.addEdge(vertex.getLabel(), neighbor.getLabel(), edge.getWeight()) return newGraph
def __eq__(self, other): """Returns True if self and other are identical, or True if self and other are of the same type, have the same number of vertices, and these vertices are connected in the same manner (including same labels and edge weights).""" # Check simple criteria first if self is other: return True if type(self) != type(other): return False if len(self) != len(other): return False for vertex in self: # All vertex labels must match if not vertex.getLabel() in other: print("Mismatched vertices") return False # Vertices with the same labels must have the same # incident edges otherVertex = other.getVertex(vertex.getLabel()) selfEdges = list(vertex.incidentEdges()) otherEdges = list(otherVertex.incidentEdges()) if len(selfEdges) != len(otherEdges): return False for edge in selfEdges: found = False for otherEdge in otherEdges: if edge == otherEdge and edge.getWeight() == otherEdge.getWeight(): found = True break if not found: return False return True
# Methods for clearing, marks, sizes, string rep
def clear(self): """Clears the graph.""" self._size = 0 self._edgeCount = 0 self._vertices = {}
def clearEdgeMarks(self): """Clears all the edge marks.""" for edge in self.edges(): edge.clearMark() def clearVertexMarks(self): """Clears all the vertex marks.""" for vertex in self.vertices(): vertex.clearMark() def sizeEdges(self): """Returns the number of edges.""" return self._edgeCount def sizeVertices(self): """Returns the number of vertices.""" return len(self) def __str__(self): """Returns the string representation of the graph.""" result = str(len(self)) + " Vertices: " for vertex in self._vertices: result += " " + str(vertex) result += " "; result += str(self.sizeEdges()) + " Edges: " for edge in self.edges(): result += " " + str(edge) return result
def add(self, label): """For compatibility with other collections.""" self.addVertex(label)
# Vertex related methods def addVertex(self, label): """Precondition: a vertex with label must not already be in the graph. Raises: AttibuteError if a vertex with label is already in the graph.""" if self.containsVertex(label): raise AttributeError("Label " + str(label) + " already in graph.""") self._vertices[label] = LinkedVertex(label) self._size += 1 def containsVertex (self, label): return label in self._vertices def getVertex(self, label): """Precondition: a vertex with label must already be in the graph. Raises: AttibuteError if a vertex with label is not already in the graph.""" if not self.containsVertex(label): raise AttributeError("Label " + str(label) + " not in graph.""") return self._vertices[label] def removeVertex(self, label): """Returns True if the vertex was removed, or False otherwise.""" removedVertex = self._vertices.pop(label, None) if removedVertex is None: return False # Examine all other vertices to remove edges # directed at the removed vertex for vertex in self.vertices(): if vertex.removeEdgeTo(removedVertex): self._edgeCount -= 1
# Examine all edges from the removed vertex to others for edge in removedVertex.incidentEdges(): self._edgeCount -= 1 self._size -= 1 return True # Methods related to edges
def addEdge(self, fromLabel, toLabel, weight): """Connects the vertices with an edge with the given weight. Preconditions: vertices with fromLabel and toLabel must already be in the graph. The vertices must not already be connected by an edge. Raises: AttibuteError if the vertices are not already in the graph or they are already connected.""" fromVertex = self.getVertex(fromLabel) toVertex = self.getVertex(toLabel) if self.getEdge(fromLabel, toLabel): raise AttributeError("An edge already connects " + str(fromLabel) + " and " + str(toLabel)) fromVertex.addEdgeTo(toVertex, weight) self._edgeCount += 1 def containsEdge(self, fromLabel, toLabel): """Returns True if an edge connects the vertices, or False otherwise.""" return self.getEdge(fromLabel, toLabel) != None def getEdge(self, fromLabel, toLabel): """Returns the edge connecting the two vertices, or None if no edge exists. Precondition: vertices with fromLabel and toLabel must already be in the graph. Raises: AttibuteError if the vertices are not already in the graph.""" fromVertex = self.getVertex(fromLabel) toVertex = self.getVertex(toLabel) return fromVertex.getEdgeTo(toVertex) def removeEdge (self, fromLabel, toLabel): """Returns True if the edge was removed, or False otherwise. Precondition: vertices with fromLabel and toLabel must already be in the graph. Raises: AttibuteError if the vertices are not already in the graph.""" fromVertex = self.getVertex(fromLabel) toVertex = self.getVertex(toLabel) edgeRemovedFlg = fromVertex.removeEdgeTo(toVertex) if edgeRemovedFlg: self._edgeCount -= 1 return edgeRemovedFlg
# Iterators def __iter__(self): """Supports iteration over a view of self (the vertices).""" return self.vertices()
def edges(self): """Supports iteration over the edges in the graph.""" result = list() for vertex in self.vertices(): result += list(vertex.incidentEdges()) return iter(result) def vertices(self): """Supports iteration over the vertices in the graph.""" return iter(self._vertices.values())
def incidentEdges(self, label): """Supports iteration over the incident edges of the given verrtex. Precondition: a vertex with label must already be in the graph. Raises: AttibuteError if a vertex with label is not already in the graph.""" return self.getVertex(label).incidentEdges() def neighboringVertices(self, label): """Supports iteration over the neighboring vertices of the given verrtex. Precondition: a vertex with label must already be in the graph. Raises: AttibuteError if a vertex with label is not already in the graph.""" return self.getVertex(label).neighboringVertices() ~~~~~~~~~~~~~~~~~
Output
Graph: 8 Vertices: H A B C E D F G 12 Edges: A>C:0 A>D:0 B>D:0 B>F:0 C>D:0 C>E:0 C>H:0 E>G:0 D>F:0 F>G:0 F>H:0 G>H:0
Courses: H A B C E D F G
Prerequisites: A>C:0 A>D:0 B>D:0 B>F:0 C>D:0 C>E:0 C>H:0 E>G:0 D>F:0 F>G:0 F>H:0 G>H:0
One possible order to take the courses: B A C E D F G H
Files in the same folder ------------------------ Depending on which implementation you use for part 1, you may need to include other files in the same folder.
Imports ------- Depending on which implementation you use for part 1, you may need to include additional import statement(s).
Part 1 ------ This week's example files include 2 different implementations of this function. Choose one and copy that implementation here.
Part 2 ------ The courses and their prerequisites are shown in a comment. The courses are each named with a single capital letter.
Part 3 ------ The courses and their prerequisites are shown in a comment.
Part 4 ------ Use an appropriate graph iterator.
Part 5 ------ Use an appropriate graph iterator.
Part 6 ------ If you used the stack-based implementation of the topologicalSort() function, it can produce a different, correct ordering for each run of your program.
The following are some of the possible correct orderings: B A C E D F G H A C E B D F G H A B C E D F G H A B C D F E G H B A C D F E G H
~~~~~~~~~~~~~~~~~~
# This program exercises graphs.
# Replace any "<your code>" comments with your own code statement(s) # to accomplish the specified task. # Do not change any other code.
# The following files must be in the same folder: # abstractcollection.py # graph.py
from graph import LinkedDirectedGraph
# Part 1: # Complete the following function: def topologicalSort(graph): # <your code>
graph = LinkedDirectedGraph()
# The graph represents the following course prerequisites: # A requires nothing # B requires nothing # C requires A # D requires A, B, and C # E requires C # F requires B and D # G requires E and F # H requires C, F, and G
# Part 2: # Add the vertices: # <your code>
# Part 3: # Add the edges: # <your code>
print("Graph:") print(graph) print()
print("Courses:") # Part 4: # Display each vertex on a separate line: # <your code> print()
print("Prerequisites:") # Part 5: # Display each edge on a separate line: # <your code> print()
print("One possible order to take the courses:") # Part 6: # Display the courses in prerequisite (topological) order: # <your code> print()
~~~~~~~~~~~~~~~
""" File: abstractcollection.py Copyright 2015 by Ken Lambert
"""
class AbstractCollection(object): """An abstract collection implementation."""
# Constructor def __init__(self, sourceCollection = None): """Sets the initial state of self, which includes the contents of sourceCollection, if it's present.""" self._size = 0 self._modCount = 0 if sourceCollection: for item in sourceCollection: self.add(item)
# Accessor methods def isEmpty(self): """Returns True if len(self) == 0, or False otherwise.""" return len(self) == 0 def __len__(self): """Returns the number of items in self.""" return self._size
def __str__(self): """Returns the string representation of self, using [] as delimiters.""" return "[" + ", ".join(map(str, self)) + "]"
def __eq__(self, other): """Returns True if self equals other, or False otherwise. Compares pairs of items in the two sequences generated by the iterators on self and other.""" if self is other: return True if type(self) != type(other) or len(self) != len(other): return False otherIter = iter(other) for item in self: if item != next(otherIter): return False return True
def __add__(self, other): """Returns a new collection containing the contents of self and other.""" result = type(self)(self) for item in other: result.add(item) return result
def count(self, item): """Returns the number of instance of item in self.""" counter = 0 for nextItem in self: if item == nextItem: counter += 1 return counter
# These methods track and update the modCount, which is used to # prevent mutations within the context of an iterator (for loop)
def getModCount(self): """Returns the number of modifications to the collection.""" return self._modCount
def incModCount(self): """Increments the number of modifications to the collection.""" self._modCount += 1
~~~~~~~~~~~~~~~
""" File: graph.py Copyright 2015 by Ken Lambert
Adds the in, +, ==, and clone operations to support graph-specific behavior. """
from abstractcollection import AbstractCollection
class LinkedEdge(object): # An edge has a source vertex, a destination vertex, # a weight, and a mark attribute.
def __init__(self, fromVertex, toVertex, weight = None): self._vertex1 = fromVertex self._vertex2 = toVertex self._weight = weight self._mark = False def clearMark(self): """Clears the mark on the edge.""" self._mark = False def __eq__(self, other): """Two edges are equal if they connect the same vertices.""" if self is other: return True if type(self) != type(other): return False return self._vertex1 == other._vertex1 and self._vertex2 == other._vertex2
def getFromVertex(self): # Derrf Seitz 12/17/2015 """Returns the edge's origin vertex.""" return self._vertex1 def getOtherVertex(self, thisVertex): """Returns the vertex opposite thisVertex.""" if thisVertex == None or thisVertex == self._vertex2: return self._vertex1 else: return self._vertex2
def getToVertex(self): """Returns the edge's destination vertex.""" return self._vertex2 def getWeight(self): """Returns the edge's weight.""" return self._weight def isMarked(self): """Returns True if the edge is marked or False otherwise.""" return self._mark def setMark(self): """Sets the mark on the edge.""" self._mark = True def setWeight(self, weight): """Sets the weight on the edge to weight.""" self._weight = weight def __str__(self): """Returns the string representation of the edge.""" return str(self._vertex1) + ">" + str(self._vertex2) + ":" + str(self._weight)
class LinkedVertex(object):
# A vertex has a label, a list of incident edges, # and a mark attribute.
def __init__(self, label): self._label = label self._edgeList = list() self._mark = False
def clearMark(self): """Clears the mark on the vertex.""" self._mark = False; def getLabel(self): """Returns the label of the vertex.""" return self._label def isMarked(self): """Returns True if the vertex is marked or False otherwise.""" return self._mark def setLabel(self, label, g): """Sets the vertex's label to label.""" g._vertices.pop(self._label, None) g._vertices[label] = self self._label = label
def setMark(self): """Sets the mark on the vertex.""" self._mark = True def __str__(self): """Returns the string representation of the vertex.""" return str(self._label)
def __eq__(self, other): """Two vertices are equal if they have the same labels.""" if self is other: return True if type(self) != type(other): return False return self.getLabel() == other.getLabel() def __hash__(self): # Derrf Seitz 12/16/2015 """Hash method for a vertex.""" return hash(self._label)
# Methods used by LinkedGraph def addEdgeTo(self, toVertex, weight): """Connects the vertices with an edge.""" edge = LinkedEdge(self, toVertex, weight) self._edgeList.append(edge) def getEdgeTo(self, toVertex): """Returns the connecting edge if it exists, or None otherwise.""" edge = LinkedEdge(self, toVertex) try: return self._edgeList[self._edgeList.index(edge)] except: return None
def incidentEdges(self): """Returns the incident edges of this vertex.""" return iter(self._edgeList) def neighboringVertices(self): """Returns the neighboring vertices of this vertex.""" vertices = list() for edge in self._edgeList: vertices.append(edge.getOtherVertex(self)) return iter(vertices) def removeEdgeTo(self, toVertex): """Returns True if the edge exists and is removed, or False otherwise.""" edge = LinkedEdge(self, toVertex) if edge in self._edgeList: self._edgeList.remove(edge) return True else: return False
class LinkedDirectedGraph(AbstractCollection):
# A graph has a count of vertices, a count of edges, # and a dictionary of label/vertex pairs.
def __init__(self, sourceCollection = None): self._edgeCount = 0 self._vertices = {} AbstractCollection.__init__(self, sourceCollection) # in, ==, +, and clone operations
def __contains__(self, item): """Returns True if item is a label in a vertex, or False otherwise.""" for vertex in self: if vertex.getLabel() == item: return True return False
def clone(self): """Returns an exact copy of self, including vertex labels, edges connecting vertices, and edge weights.""" newGraph = type(self)(map(lambda vertex: vertex.getLabel(), self)) for vertex in self: for neighbor in vertex.neighboringVertices(): edge = vertex.getEdgeTo(neighbor) newGraph.addEdge(vertex.getLabel(), neighbor.getLabel(), edge.getWeight()) return newGraph
def __add__(self, other): """Returns a new collection consisting of the items in self and other. This will be a graph with the contents of the operand graphs as separate components. Precondition: the labels in the two graph operands are disjoint. Raises: AttributeError if the labels in the two operand graphs are not disjoint.""" newGraph = self.clone() for vertex in other: newGraph.add(vertex.getLabel()) for vertex in other: for neighbor in vertex.neighboringVertices(): edge = vertex.getEdgeTo(neighbor) newGraph.addEdge(vertex.getLabel(), neighbor.getLabel(), edge.getWeight()) return newGraph
def __eq__(self, other): """Returns True if self and other are identical, or True if self and other are of the same type, have the same number of vertices, and these vertices are connected in the same manner (including same labels and edge weights).""" # Check simple criteria first if self is other: return True if type(self) != type(other): return False if len(self) != len(other): return False for vertex in self: # All vertex labels must match if not vertex.getLabel() in other: print("Mismatched vertices") return False # Vertices with the same labels must have the same # incident edges otherVertex = other.getVertex(vertex.getLabel()) selfEdges = list(vertex.incidentEdges()) otherEdges = list(otherVertex.incidentEdges()) if len(selfEdges) != len(otherEdges): return False for edge in selfEdges: found = False for otherEdge in otherEdges: if edge == otherEdge and edge.getWeight() == otherEdge.getWeight(): found = True break if not found: return False return True
# Methods for clearing, marks, sizes, string rep
def clear(self): """Clears the graph.""" self._size = 0 self._edgeCount = 0 self._vertices = {}
def clearEdgeMarks(self): """Clears all the edge marks.""" for edge in self.edges(): edge.clearMark() def clearVertexMarks(self): """Clears all the vertex marks.""" for vertex in self.vertices(): vertex.clearMark() def sizeEdges(self): """Returns the number of edges.""" return self._edgeCount def sizeVertices(self): """Returns the number of vertices.""" return len(self) def __str__(self): """Returns the string representation of the graph.""" result = str(len(self)) + " Vertices: " for vertex in self._vertices: result += " " + str(vertex) result += " "; result += str(self.sizeEdges()) + " Edges: " for edge in self.edges(): result += " " + str(edge) return result
def add(self, label): """For compatibility with other collections.""" self.addVertex(label)
# Vertex related methods def addVertex(self, label): """Precondition: a vertex with label must not already be in the graph. Raises: AttibuteError if a vertex with label is already in the graph.""" if self.containsVertex(label): raise AttributeError("Label " + str(label) + " already in graph.""") self._vertices[label] = LinkedVertex(label) self._size += 1 def containsVertex (self, label): return label in self._vertices def getVertex(self, label): """Precondition: a vertex with label must already be in the graph. Raises: AttibuteError if a vertex with label is not already in the graph.""" if not self.containsVertex(label): raise AttributeError("Label " + str(label) + " not in graph.""") return self._vertices[label] def removeVertex(self, label): """Returns True if the vertex was removed, or False otherwise.""" removedVertex = self._vertices.pop(label, None) if removedVertex is None: return False # Examine all other vertices to remove edges # directed at the removed vertex for vertex in self.vertices(): if vertex.removeEdgeTo(removedVertex): self._edgeCount -= 1
# Examine all edges from the removed vertex to others for edge in removedVertex.incidentEdges(): self._edgeCount -= 1 self._size -= 1 return True # Methods related to edges
def addEdge(self, fromLabel, toLabel, weight): """Connects the vertices with an edge with the given weight. Preconditions: vertices with fromLabel and toLabel must already be in the graph. The vertices must not already be connected by an edge. Raises: AttibuteError if the vertices are not already in the graph or they are already connected.""" fromVertex = self.getVertex(fromLabel) toVertex = self.getVertex(toLabel) if self.getEdge(fromLabel, toLabel): raise AttributeError("An edge already connects " + str(fromLabel) + " and " + str(toLabel)) fromVertex.addEdgeTo(toVertex, weight) self._edgeCount += 1 def containsEdge(self, fromLabel, toLabel): """Returns True if an edge connects the vertices, or False otherwise.""" return self.getEdge(fromLabel, toLabel) != None def getEdge(self, fromLabel, toLabel): """Returns the edge connecting the two vertices, or None if no edge exists. Precondition: vertices with fromLabel and toLabel must already be in the graph. Raises: AttibuteError if the vertices are not already in the graph.""" fromVertex = self.getVertex(fromLabel) toVertex = self.getVertex(toLabel) return fromVertex.getEdgeTo(toVertex) def removeEdge (self, fromLabel, toLabel): """Returns True if the edge was removed, or False otherwise. Precondition: vertices with fromLabel and toLabel must already be in the graph. Raises: AttibuteError if the vertices are not already in the graph.""" fromVertex = self.getVertex(fromLabel) toVertex = self.getVertex(toLabel) edgeRemovedFlg = fromVertex.removeEdgeTo(toVertex) if edgeRemovedFlg: self._edgeCount -= 1 return edgeRemovedFlg
# Iterators def __iter__(self): """Supports iteration over a view of self (the vertices).""" return self.vertices()
def edges(self): """Supports iteration over the edges in the graph.""" result = list() for vertex in self.vertices(): result += list(vertex.incidentEdges()) return iter(result) def vertices(self): """Supports iteration over the vertices in the graph.""" return iter(self._vertices.values())
def incidentEdges(self, label): """Supports iteration over the incident edges of the given verrtex. Precondition: a vertex with label must already be in the graph. Raises: AttibuteError if a vertex with label is not already in the graph.""" return self.getVertex(label).incidentEdges() def neighboringVertices(self, label): """Supports iteration over the neighboring vertices of the given verrtex. Precondition: a vertex with label must already be in the graph. Raises: AttibuteError if a vertex with label is not already in the graph.""" return self.getVertex(label).neighboringVertices() ~~~~~~~~~~~~~~~~~
Output
Graph: 8 Vertices: H A B C E D F G 12 Edges: A>C:0 A>D:0 B>D:0 B>F:0 C>D:0 C>E:0 C>H:0 E>G:0 D>F:0 F>G:0 F>H:0 G>H:0
Courses: H A B C E D F G
Prerequisites: A>C:0 A>D:0 B>D:0 B>F:0 C>D:0 C>E:0 C>H:0 E>G:0 D>F:0 F>G:0 F>H:0 G>H:0
One possible order to take the courses: B A C E D F G H
Files in the same folder ------------------------ Depending on which implementation you use for part 1, you may need to include other files in the same folder.
Imports ------- Depending on which implementation you use for part 1, you may need to include additional import statement(s).
Part 1 ------ This week's example files include 2 different implementations of this function. Choose one and copy that implementation here.
Part 2 ------ The courses and their prerequisites are shown in a comment. The courses are each named with a single capital letter.
Part 3 ------ The courses and their prerequisites are shown in a comment.
Part 4 ------ Use an appropriate graph iterator.
Part 5 ------ Use an appropriate graph iterator.
Part 6 ------ If you used the stack-based implementation of the topologicalSort() function, it can produce a different, correct ordering for each run of your program.
The following are some of the possible correct orderings: B A C E D F G H A C E B D F G H A B C E D F G H A B C D F E G H B A C D F E G H
~~~~~~~~~~~~~~~~~~
# This program exercises graphs.
# Replace any "<your code>" comments with your own code statement(s) # to accomplish the specified task. # Do not change any other code.
# The following files must be in the same folder: # abstractcollection.py # graph.py
from graph import LinkedDirectedGraph
# Part 1: # Complete the following function: def topologicalSort(graph): # <your code>
graph = LinkedDirectedGraph()
# The graph represents the following course prerequisites: # A requires nothing # B requires nothing # C requires A # D requires A, B, and C # E requires C # F requires B and D # G requires E and F # H requires C, F, and G
# Part 2: # Add the vertices: # <your code>
# Part 3: # Add the edges: # <your code>
print("Graph:") print(graph) print()
print("Courses:") # Part 4: # Display each vertex on a separate line: # <your code> print()
print("Prerequisites:") # Part 5: # Display each edge on a separate line: # <your code> print()
print("One possible order to take the courses:") # Part 6: # Display the courses in prerequisite (topological) order: # <your code> print()
~~~~~~~~~~~~~~~
""" File: abstractcollection.py Copyright 2015 by Ken Lambert
"""
class AbstractCollection(object): """An abstract collection implementation."""
# Constructor def __init__(self, sourceCollection = None): """Sets the initial state of self, which includes the contents of sourceCollection, if it's present.""" self._size = 0 self._modCount = 0 if sourceCollection: for item in sourceCollection: self.add(item)
# Accessor methods def isEmpty(self): """Returns True if len(self) == 0, or False otherwise.""" return len(self) == 0 def __len__(self): """Returns the number of items in self.""" return self._size
def __str__(self): """Returns the string representation of self, using [] as delimiters.""" return "[" + ", ".join(map(str, self)) + "]"
def __eq__(self, other): """Returns True if self equals other, or False otherwise. Compares pairs of items in the two sequences generated by the iterators on self and other.""" if self is other: return True if type(self) != type(other) or len(self) != len(other): return False otherIter = iter(other) for item in self: if item != next(otherIter): return False return True
def __add__(self, other): """Returns a new collection containing the contents of self and other.""" result = type(self)(self) for item in other: result.add(item) return result
def count(self, item): """Returns the number of instance of item in self.""" counter = 0 for nextItem in self: if item == nextItem: counter += 1 return counter
# These methods track and update the modCount, which is used to # prevent mutations within the context of an iterator (for loop)
def getModCount(self): """Returns the number of modifications to the collection.""" return self._modCount
def incModCount(self): """Increments the number of modifications to the collection.""" self._modCount += 1
~~~~~~~~~~~~~~~
""" File: graph.py Copyright 2015 by Ken Lambert
Adds the in, +, ==, and clone operations to support graph-specific behavior. """
from abstractcollection import AbstractCollection
class LinkedEdge(object): # An edge has a source vertex, a destination vertex, # a weight, and a mark attribute.
def __init__(self, fromVertex, toVertex, weight = None): self._vertex1 = fromVertex self._vertex2 = toVertex self._weight = weight self._mark = False def clearMark(self): """Clears the mark on the edge.""" self._mark = False def __eq__(self, other): """Two edges are equal if they connect the same vertices.""" if self is other: return True if type(self) != type(other): return False return self._vertex1 == other._vertex1 and self._vertex2 == other._vertex2
def getFromVertex(self): # Derrf Seitz 12/17/2015 """Returns the edge's origin vertex.""" return self._vertex1 def getOtherVertex(self, thisVertex): """Returns the vertex opposite thisVertex.""" if thisVertex == None or thisVertex == self._vertex2: return self._vertex1 else: return self._vertex2
def getToVertex(self): """Returns the edge's destination vertex.""" return self._vertex2 def getWeight(self): """Returns the edge's weight.""" return self._weight def isMarked(self): """Returns True if the edge is marked or False otherwise.""" return self._mark def setMark(self): """Sets the mark on the edge.""" self._mark = True def setWeight(self, weight): """Sets the weight on the edge to weight.""" self._weight = weight def __str__(self): """Returns the string representation of the edge.""" return str(self._vertex1) + ">" + str(self._vertex2) + ":" + str(self._weight)
class LinkedVertex(object):
# A vertex has a label, a list of incident edges, # and a mark attribute.
def __init__(self, label): self._label = label self._edgeList = list() self._mark = False
def clearMark(self): """Clears the mark on the vertex.""" self._mark = False; def getLabel(self): """Returns the label of the vertex.""" return self._label def isMarked(self): """Returns True if the vertex is marked or False otherwise.""" return self._mark def setLabel(self, label, g): """Sets the vertex's label to label.""" g._vertices.pop(self._label, None) g._vertices[label] = self self._label = label
def setMark(self): """Sets the mark on the vertex.""" self._mark = True def __str__(self): """Returns the string representation of the vertex.""" return str(self._label)
def __eq__(self, other): """Two vertices are equal if they have the same labels.""" if self is other: return True if type(self) != type(other): return False return self.getLabel() == other.getLabel() def __hash__(self): # Derrf Seitz 12/16/2015 """Hash method for a vertex.""" return hash(self._label)
# Methods used by LinkedGraph def addEdgeTo(self, toVertex, weight): """Connects the vertices with an edge.""" edge = LinkedEdge(self, toVertex, weight) self._edgeList.append(edge) def getEdgeTo(self, toVertex): """Returns the connecting edge if it exists, or None otherwise.""" edge = LinkedEdge(self, toVertex) try: return self._edgeList[self._edgeList.index(edge)] except: return None
def incidentEdges(self): """Returns the incident edges of this vertex.""" return iter(self._edgeList) def neighboringVertices(self): """Returns the neighboring vertices of this vertex.""" vertices = list() for edge in self._edgeList: vertices.append(edge.getOtherVertex(self)) return iter(vertices) def removeEdgeTo(self, toVertex): """Returns True if the edge exists and is removed, or False otherwise.""" edge = LinkedEdge(self, toVertex) if edge in self._edgeList: self._edgeList.remove(edge) return True else: return False
class LinkedDirectedGraph(AbstractCollection):
# A graph has a count of vertices, a count of edges, # and a dictionary of label/vertex pairs.
def __init__(self, sourceCollection = None): self._edgeCount = 0 self._vertices = {} AbstractCollection.__init__(self, sourceCollection) # in, ==, +, and clone operations
def __contains__(self, item): """Returns True if item is a label in a vertex, or False otherwise.""" for vertex in self: if vertex.getLabel() == item: return True return False
def clone(self): """Returns an exact copy of self, including vertex labels, edges connecting vertices, and edge weights.""" newGraph = type(self)(map(lambda vertex: vertex.getLabel(), self)) for vertex in self: for neighbor in vertex.neighboringVertices(): edge = vertex.getEdgeTo(neighbor) newGraph.addEdge(vertex.getLabel(), neighbor.getLabel(), edge.getWeight()) return newGraph
def __add__(self, other): """Returns a new collection consisting of the items in self and other. This will be a graph with the contents of the operand graphs as separate components. Precondition: the labels in the two graph operands are disjoint. Raises: AttributeError if the labels in the two operand graphs are not disjoint.""" newGraph = self.clone() for vertex in other: newGraph.add(vertex.getLabel()) for vertex in other: for neighbor in vertex.neighboringVertices(): edge = vertex.getEdgeTo(neighbor) newGraph.addEdge(vertex.getLabel(), neighbor.getLabel(), edge.getWeight()) return newGraph
def __eq__(self, other): """Returns True if self and other are identical, or True if self and other are of the same type, have the same number of vertices, and these vertices are connected in the same manner (including same labels and edge weights).""" # Check simple criteria first if self is other: return True if type(self) != type(other): return False if len(self) != len(other): return False for vertex in self: # All vertex labels must match if not vertex.getLabel() in other: print("Mismatched vertices") return False # Vertices with the same labels must have the same # incident edges otherVertex = other.getVertex(vertex.getLabel()) selfEdges = list(vertex.incidentEdges()) otherEdges = list(otherVertex.incidentEdges()) if len(selfEdges) != len(otherEdges): return False for edge in selfEdges: found = False for otherEdge in otherEdges: if edge == otherEdge and edge.getWeight() == otherEdge.getWeight(): found = True break if not found: return False return True
# Methods for clearing, marks, sizes, string rep
def clear(self): """Clears the graph.""" self._size = 0 self._edgeCount = 0 self._vertices = {}
def clearEdgeMarks(self): """Clears all the edge marks.""" for edge in self.edges(): edge.clearMark() def clearVertexMarks(self): """Clears all the vertex marks.""" for vertex in self.vertices(): vertex.clearMark() def sizeEdges(self): """Returns the number of edges.""" return self._edgeCount def sizeVertices(self): """Returns the number of vertices.""" return len(self) def __str__(self): """Returns the string representation of the graph.""" result = str(len(self)) + " Vertices: " for vertex in self._vertices: result += " " + str(vertex) result += " "; result += str(self.sizeEdges()) + " Edges: " for edge in self.edges(): result += " " + str(edge) return result
def add(self, label): """For compatibility with other collections.""" self.addVertex(label)
# Vertex related methods def addVertex(self, label): """Precondition: a vertex with label must not already be in the graph. Raises: AttibuteError if a vertex with label is already in the graph.""" if self.containsVertex(label): raise AttributeError("Label " + str(label) + " already in graph.""") self._vertices[label] = LinkedVertex(label) self._size += 1 def containsVertex (self, label): return label in self._vertices def getVertex(self, label): """Precondition: a vertex with label must already be in the graph. Raises: AttibuteError if a vertex with label is not already in the graph.""" if not self.containsVertex(label): raise AttributeError("Label " + str(label) + " not in graph.""") return self._vertices[label] def removeVertex(self, label): """Returns True if the vertex was removed, or False otherwise.""" removedVertex = self._vertices.pop(label, None) if removedVertex is None: return False # Examine all other vertices to remove edges # directed at the removed vertex for vertex in self.vertices(): if vertex.removeEdgeTo(removedVertex): self._edgeCount -= 1
# Examine all edges from the removed vertex to others for edge in removedVertex.incidentEdges(): self._edgeCount -= 1 self._size -= 1 return True # Methods related to edges
def addEdge(self, fromLabel, toLabel, weight): """Connects the vertices with an edge with the given weight. Preconditions: vertices with fromLabel and toLabel must already be in the graph. The vertices must not already be connected by an edge. Raises: AttibuteError if the vertices are not already in the graph or they are already connected.""" fromVertex = self.getVertex(fromLabel) toVertex = self.getVertex(toLabel) if self.getEdge(fromLabel, toLabel): raise AttributeError("An edge already connects " + str(fromLabel) + " and " + str(toLabel)) fromVertex.addEdgeTo(toVertex, weight) self._edgeCount += 1 def containsEdge(self, fromLabel, toLabel): """Returns True if an edge connects the vertices, or False otherwise.""" return self.getEdge(fromLabel, toLabel) != None def getEdge(self, fromLabel, toLabel): """Returns the edge connecting the two vertices, or None if no edge exists. Precondition: vertices with fromLabel and toLabel must already be in the graph. Raises: AttibuteError if the vertices are not already in the graph.""" fromVertex = self.getVertex(fromLabel) toVertex = self.getVertex(toLabel) return fromVertex.getEdgeTo(toVertex) def removeEdge (self, fromLabel, toLabel): """Returns True if the edge was removed, or False otherwise. Precondition: vertices with fromLabel and toLabel must already be in the graph. Raises: AttibuteError if the vertices are not already in the graph.""" fromVertex = self.getVertex(fromLabel) toVertex = self.getVertex(toLabel) edgeRemovedFlg = fromVertex.removeEdgeTo(toVertex) if edgeRemovedFlg: self._edgeCount -= 1 return edgeRemovedFlg
# Iterators def __iter__(self): """Supports iteration over a view of self (the vertices).""" return self.vertices()
def edges(self): """Supports iteration over the edges in the graph.""" result = list() for vertex in self.vertices(): result += list(vertex.incidentEdges()) return iter(result) def vertices(self): """Supports iteration over the vertices in the graph.""" return iter(self._vertices.values())
def incidentEdges(self, label): """Supports iteration over the incident edges of the given verrtex. Precondition: a vertex with label must already be in the graph. Raises: AttibuteError if a vertex with label is not already in the graph.""" return self.getVertex(label).incidentEdges() def neighboringVertices(self, label): """Supports iteration over the neighboring vertices of the given verrtex. Precondition: a vertex with label must already be in the graph. Raises: AttibuteError if a vertex with label is not already in the graph.""" return self.getVertex(label).neighboringVertices() ~~~~~~~~~~~~~~~~~
Output
Graph: 8 Vertices: H A B C E D F G 12 Edges: A>C:0 A>D:0 B>D:0 B>F:0 C>D:0 C>E:0 C>H:0 E>G:0 D>F:0 F>G:0 F>H:0 G>H:0
Courses: H A B C E D F G
Prerequisites: A>C:0 A>D:0 B>D:0 B>F:0 C>D:0 C>E:0 C>H:0 E>G:0 D>F:0 F>G:0 F>H:0 G>H:0
One possible order to take the courses: B A C E D F G H
Files in the same folder ------------------------ Depending on which implementation you use for part 1, you may need to include other files in the same folder.
Imports ------- Depending on which implementation you use for part 1, you may need to include additional import statement(s).
Part 1 ------ This week's example files include 2 different implementations of this function. Choose one and copy that implementation here.
Part 2 ------ The courses and their prerequisites are shown in a comment. The courses are each named with a single capital letter.
Part 3 ------ The courses and their prerequisites are shown in a comment.
Part 4 ------ Use an appropriate graph iterator.
Part 5 ------ Use an appropriate graph iterator.
Part 6 ------ If you used the stack-based implementation of the topologicalSort() function, it can produce a different, correct ordering for each run of your program.
The following are some of the possible correct orderings: B A C E D F G H A C E B D F G H A B C E D F G H A B C D F E G H B A C D F E G H
~~~~~~~~~~~~~~~~~~
# This program exercises graphs.
# Replace any "<your code>" comments with your own code statement(s) # to accomplish the specified task. # Do not change any other code.
# The following files must be in the same folder: # abstractcollection.py # graph.py
from graph import LinkedDirectedGraph
# Part 1: # Complete the following function: def topologicalSort(graph): # <your code>
graph = LinkedDirectedGraph()
# The graph represents the following course prerequisites: # A requires nothing # B requires nothing # C requires A # D requires A, B, and C # E requires C # F requires B and D # G requires E and F # H requires C, F, and G
# Part 2: # Add the vertices: # <your code>
# Part 3: # Add the edges: # <your code>
print("Graph:") print(graph) print()
print("Courses:") # Part 4: # Display each vertex on a separate line: # <your code> print()
print("Prerequisites:") # Part 5: # Display each edge on a separate line: # <your code> print()
print("One possible order to take the courses:") # Part 6: # Display the courses in prerequisite (topological) order: # <your code> print() # This program exercises graphs.
# Replace any "<your code>" comments with your own code statement(s) # to accomplish the specified task. # Do not change any other code.
# The following files must be in the same folder: # abstractcollection.py # graph.py
from graph import LinkedDirectedGraph
# Part 1: # Complete the following function: def topologicalSort(graph): # <your code>
graph = LinkedDirectedGraph()
# The graph represents the following course prerequisites: # A requires nothing # B requires nothing # C requires A # D requires A, B, and C # E requires C # F requires B and D # G requires E and F # H requires C, F, and G
# Part 2: # Add the vertices: # <your code>
# Part 3: # Add the edges: # <your code>
print("Graph:") print(graph) print()
print("Courses:") # Part 4: # Display each vertex on a separate line: # <your code> print()
print("Prerequisites:") # Part 5: # Display each edge on a separate line: # <your code> print()
print("One possible order to take the courses:") # Part 6: # Display the courses in prerequisite (topological) order: # <your code> print()
~~~~~~~~~~~~~~~
""" File: abstractcollection.py Copyright 2015 by Ken Lambert
"""
class AbstractCollection(object): """An abstract collection implementation."""
# Constructor def __init__(self, sourceCollection = None): """Sets the initial state of self, which includes the contents of sourceCollection, if it's present.""" self._size = 0 self._modCount = 0 if sourceCollection: for item in sourceCollection: self.add(item)
# Accessor methods def isEmpty(self): """Returns True if len(self) == 0, or False otherwise.""" return len(self) == 0 def __len__(self): """Returns the number of items in self.""" return self._size
def __str__(self): """Returns the string representation of self, using [] as delimiters.""" return "[" + ", ".join(map(str, self)) + "]"
def __eq__(self, other): """Returns True if self equals other, or False otherwise. Compares pairs of items in the two sequences generated by the iterators on self and other.""" if self is other: return True if type(self) != type(other) or len(self) != len(other): return False otherIter = iter(other) for item in self: if item != next(otherIter): return False return True
def __add__(self, other): """Returns a new collection containing the contents of self and other.""" result = type(self)(self) for item in other: result.add(item) return result
def count(self, item): """Returns the number of instance of item in self.""" counter = 0 for nextItem in self: if item == nextItem: counter += 1 return counter
# These methods track and update the modCount, which is used to # prevent mutations within the context of an iterator (for loop)
def getModCount(self): """Returns the number of modifications to the collection.""" return self._modCount
def incModCount(self): """Increments the number of modifications to the collection.""" self._modCount += 1 """ File: abstractcollection.py Copyright 2015 by Ken Lambert
"""
class AbstractCollection(object): """An abstract collection implementation."""
# Constructor def __init__(self, sourceCollection = None): """Sets the initial state of self, which includes the contents of sourceCollection, if it's present.""" self._size = 0 self._modCount = 0 if sourceCollection: for item in sourceCollection: self.add(item)
# Accessor methods def isEmpty(self): """Returns True if len(self) == 0, or False otherwise.""" return len(self) == 0 def __len__(self): """Returns the number of items in self.""" return self._size
def __str__(self): """Returns the string representation of self, using [] as delimiters.""" return "[" + ", ".join(map(str, self)) + "]"
def __eq__(self, other): """Returns True if self equals other, or False otherwise. Compares pairs of items in the two sequences generated by the iterators on self and other.""" if self is other: return True if type(self) != type(other) or len(self) != len(other): return False otherIter = iter(other) for item in self: if item != next(otherIter): return False return True
def __add__(self, other): """Returns a new collection containing the contents of self and other.""" result = type(self)(self) for item in other: result.add(item) return result
def count(self, item): """Returns the number of instance of item in self.""" counter = 0 for nextItem in self: if item == nextItem: counter += 1 return counter
# These methods track and update the modCount, which is used to # prevent mutations within the context of an iterator (for loop)
def getModCount(self): """Returns the number of modifications to the collection.""" return self._modCount
def incModCount(self): """Increments the number of modifications to the collection.""" self._modCount += 1
~~~~~~~~~~~~~~~
""" File: graph.py Copyright 2015 by Ken Lambert
Adds the in, +, ==, and clone operations to support graph-specific behavior. """
from abstractcollection import AbstractCollection
class LinkedEdge(object): # An edge has a source vertex, a destination vertex, # a weight, and a mark attribute.
def __init__(self, fromVertex, toVertex, weight = None): self._vertex1 = fromVertex self._vertex2 = toVertex self._weight = weight self._mark = False def clearMark(self): """Clears the mark on the edge.""" self._mark = False def __eq__(self, other): """Two edges are equal if they connect the same vertices.""" if self is other: return True if type(self) != type(other): return False return self._vertex1 == other._vertex1 and self._vertex2 == other._vertex2
def getFromVertex(self): # Derrf Seitz 12/17/2015 """Returns the edge's origin vertex.""" return self._vertex1 def getOtherVertex(self, thisVertex): """Returns the vertex opposite thisVertex.""" if thisVertex == None or thisVertex == self._vertex2: return self._vertex1 else: return self._vertex2
def getToVertex(self): """Returns the edge's destination vertex.""" return self._vertex2 def getWeight(self): """Returns the edge's weight.""" return self._weight def isMarked(self): """Returns True if the edge is marked or False otherwise.""" return self._mark def setMark(self): """Sets the mark on the edge.""" self._mark = True def setWeight(self, weight): """Sets the weight on the edge to weight.""" self._weight = weight def __str__(self): """Returns the string representation of the edge.""" return str(self._vertex1) + ">" + str(self._vertex2) + ":" + str(self._weight)
class LinkedVertex(object):
# A vertex has a label, a list of incident edges, # and a mark attribute.
def __init__(self, label): self._label = label self._edgeList = list() self._mark = False
def clearMark(self): """Clears the mark on the vertex.""" self._mark = False; def getLabel(self): """Returns the label of the vertex.""" return self._label def isMarked(self): """Returns True if the vertex is marked or False otherwise.""" return self._mark def setLabel(self, label, g): """Sets the vertex's label to label.""" g._vertices.pop(self._label, None) g._vertices[label] = self self._label = label
def setMark(self): """Sets the mark on the vertex.""" self._mark = True def __str__(self): """Returns the string representation of the vertex.""" return str(self._label)
def __eq__(self, other): """Two vertices are equal if they have the same labels.""" if self is other: return True if type(self) != type(other): return False return self.getLabel() == other.getLabel() def __hash__(self): # Derrf Seitz 12/16/2015 """Hash method for a vertex.""" return hash(self._label)
# Methods used by LinkedGraph def addEdgeTo(self, toVertex, weight): """Connects the vertices with an edge.""" edge = LinkedEdge(self, toVertex, weight) self._edgeList.append(edge) def getEdgeTo(self, toVertex): """Returns the connecting edge if it exists, or None otherwise.""" edge = LinkedEdge(self, toVertex) try: return self._edgeList[self._edgeList.index(edge)] except: return None
def incidentEdges(self): """Returns the incident edges of this vertex.""" return iter(self._edgeList) def neighboringVertices(self): """Returns the neighboring vertices of this vertex.""" vertices = list() for edge in self._edgeList: vertices.append(edge.getOtherVertex(self)) return iter(vertices) def removeEdgeTo(self, toVertex): """Returns True if the edge exists and is removed, or False otherwise.""" edge = LinkedEdge(self, toVertex) if edge in self._edgeList: self._edgeList.remove(edge) return True else: return False
class LinkedDirectedGraph(AbstractCollection):
# A graph has a count of vertices, a count of edges, # and a dictionary of label/vertex pairs.
def __init__(self, sourceCollection = None): self._edgeCount = 0 self._vertices = {} AbstractCollection.__init__(self, sourceCollection) # in, ==, +, and clone operations
def __contains__(self, item): """Returns True if item is a label in a vertex, or False otherwise.""" for vertex in self: if vertex.getLabel() == item: return True return False
def clone(self): """Returns an exact copy of self, including vertex labels, edges connecting vertices, and edge weights.""" newGraph = type(self)(map(lambda vertex: vertex.getLabel(), self)) for vertex in self: for neighbor in vertex.neighboringVertices(): edge = vertex.getEdgeTo(neighbor) newGraph.addEdge(vertex.getLabel(), neighbor.getLabel(), edge.getWeight()) return newGraph
def __add__(self, other): """Returns a new collection consisting of the items in self and other. This will be a graph with the contents of the operand graphs as separate components. Precondition: the labels in the two graph operands are disjoint. Raises: AttributeError if the labels in the two operand graphs are not disjoint.""" newGraph = self.clone() for vertex in other: newGraph.add(vertex.getLabel()) for vertex in other: for neighbor in vertex.neighboringVertices(): edge = vertex.getEdgeTo(neighbor) newGraph.addEdge(vertex.getLabel(), neighbor.getLabel(), edge.getWeight()) return newGraph
def __eq__(self, other): """Returns True if self and other are identical, or True if self and other are of the same type, have the same number of vertices, and these vertices are connected in the same manner (including same labels and edge weights).""" # Check simple criteria first if self is other: return True if type(self) != type(other): return False if len(self) != len(other): return False for vertex in self: # All vertex labels must match if not vertex.getLabel() in other: print("Mismatched vertices") return False # Vertices with the same labels must have the same # incident edges otherVertex = other.getVertex(vertex.getLabel()) selfEdges = list(vertex.incidentEdges()) otherEdges = list(otherVertex.incidentEdges()) if len(selfEdges) != len(otherEdges): return False for edge in selfEdges: found = False for otherEdge in otherEdges: if edge == otherEdge and edge.getWeight() == otherEdge.getWeight(): found = True break if not found: return False return True
# Methods for clearing, marks, sizes, string rep
def clear(self): """Clears the graph.""" self._size = 0 self._edgeCount = 0 self._vertices = {}
def clearEdgeMarks(self): """Clears all the edge marks.""" for edge in self.edges(): edge.clearMark() def clearVertexMarks(self): """Clears all the vertex marks.""" for vertex in self.vertices(): vertex.clearMark() def sizeEdges(self): """Returns the number of edges.""" return self._edgeCount def sizeVertices(self): """Returns the number of vertices.""" return len(self) def __str__(self): """Returns the string representation of the graph.""" result = str(len(self)) + " Vertices: " for vertex in self._vertices: result += " " + str(vertex) result += " "; result += str(self.sizeEdges()) + " Edges: " for edge in self.edges(): result += " " + str(edge) return result
def add(self, label): """For compatibility with other collections.""" self.addVertex(label)
# Vertex related methods def addVertex(self, label): """Precondition: a vertex with label must not already be in the graph. Raises: AttibuteError if a vertex with label is already in the graph.""" if self.containsVertex(label): raise AttributeError("Label " + str(label) + " already in graph.""") self._vertices[label] = LinkedVertex(label) self._size += 1 def containsVertex (self, label): return label in self._vertices def getVertex(self, label): """Precondition: a vertex with label must already be in the graph. Raises: AttibuteError if a vertex with label is not already in the graph.""" if not self.containsVertex(label): raise AttributeError("Label " + str(label) + " not in graph.""") return self._vertices[label] def removeVertex(self, label): """Returns True if the vertex was removed, or False otherwise.""" removedVertex = self._vertices.pop(label, None) if removedVertex is None: return False # Examine all other vertices to remove edges # directed at the removed vertex for vertex in self.vertices(): if vertex.removeEdgeTo(removedVertex): self._edgeCount -= 1
# Examine all edges from the removed vertex to others for edge in removedVertex.incidentEdges(): self._edgeCount -= 1 self._size -= 1 return True # Methods related to edges
def addEdge(self, fromLabel, toLabel, weight): """Connects the vertices with an edge with the given weight. Preconditions: vertices with fromLabel and toLabel must already be in the graph. The vertices must not already be connected by an edge. Raises: AttibuteError if the vertices are not already in the graph or they are already connected.""" fromVertex = self.getVertex(fromLabel) toVertex = self.getVertex(toLabel) if self.getEdge(fromLabel, toLabel): raise AttributeError("An edge already connects " + str(fromLabel) + " and " + str(toLabel)) fromVertex.addEdgeTo(toVertex, weight) self._edgeCount += 1 def containsEdge(self, fromLabel, toLabel): """Returns True if an edge connects the vertices, or False otherwise.""" return self.getEdge(fromLabel, toLabel) != None def getEdge(self, fromLabel, toLabel): """Returns the edge connecting the two vertices, or None if no edge exists. Precondition: vertices with fromLabel and toLabel must already be in the graph. Raises: AttibuteError if the vertices are not already in the graph.""" fromVertex = self.getVertex(fromLabel) toVertex = self.getVertex(toLabel) return fromVertex.getEdgeTo(toVertex) def removeEdge (self, fromLabel, toLabel): """Returns True if the edge was removed, or False otherwise. Precondition: vertices with fromLabel and toLabel must already be in the graph. Raises: AttibuteError if the vertices are not already in the graph.""" fromVertex = self.getVertex(fromLabel) toVertex = self.getVertex(toLabel) edgeRemovedFlg = fromVertex.removeEdgeTo(toVertex) if edgeRemovedFlg: self._edgeCount -= 1 return edgeRemovedFlg
# Iterators def __iter__(self): """Supports iteration over a view of self (the vertices).""" return self.vertices()
def edges(self): """Supports iteration over the edges in the graph.""" result = list() for vertex in self.vertices(): result += list(vertex.incidentEdges()) return iter(result) def vertices(self): """Supports iteration over the vertices in the graph.""" return iter(self._vertices.values())
def incidentEdges(self, label): """Supports iteration over the incident edges of the given verrtex. Precondition: a vertex with label must already be in the graph. Raises: AttibuteError if a vertex with label is not already in the graph.""" return self.getVertex(label).incidentEdges() def neighboringVertices(self, label): """Supports iteration over the neighboring vertices of the given verrtex. Precondition: a vertex with label must already be in the graph. Raises: AttibuteError if a vertex with label is not already in the graph.""" return self.getVertex(label).neighboringVertices() """ File: graph.py Copyright 2015 by Ken Lambert
Adds the in, +, ==, and clone operations to support graph-specific behavior. """
from abstractcollection import AbstractCollection
class LinkedEdge(object): # An edge has a source vertex, a destination vertex, # a weight, and a mark attribute.
def __init__(self, fromVertex, toVertex, weight = None): self._vertex1 = fromVertex self._vertex2 = toVertex self._weight = weight self._mark = False def clearMark(self): """Clears the mark on the edge.""" self._mark = False def __eq__(self, other): """Two edges are equal if they connect the same vertices.""" if self is other: return True if type(self) != type(other): return False return self._vertex1 == other._vertex1 and self._vertex2 == other._vertex2
def getFromVertex(self): # Derrf Seitz 12/17/2015 """Returns the edge's origin vertex.""" return self._vertex1 def getOtherVertex(self, thisVertex): """Returns the vertex opposite thisVertex.""" if thisVertex == None or thisVertex == self._vertex2: return self._vertex1 else: return self._vertex2
def getToVertex(self): """Returns the edge's destination vertex.""" return self._vertex2 def getWeight(self): """Returns the edge's weight.""" return self._weight def isMarked(self): """Returns True if the edge is marked or False otherwise.""" return self._mark def setMark(self): """Sets the mark on the edge.""" self._mark = True def setWeight(self, weight): """Sets the weight on the edge to weight.""" self._weight = weight def __str__(self): """Returns the string representation of the edge.""" return str(self._vertex1) + ">" + str(self._vertex2) + ":" + str(self._weight)
class LinkedVertex(object):
# A vertex has a label, a list of incident edges, # and a mark attribute.
def __init__(self, label): self._label = label self._edgeList = list() self._mark = False
def clearMark(self): """Clears the mark on the vertex.""" self._mark = False; def getLabel(self): """Returns the label of the vertex.""" return self._label def isMarked(self): """Returns True if the vertex is marked or False otherwise.""" return self._mark def setLabel(self, label, g): """Sets the vertex's label to label.""" g._vertices.pop(self._label, None) g._vertices[label] = self self._label = label
def setMark(self): """Sets the mark on the vertex.""" self._mark = True def __str__(self): """Returns the string representation of the vertex.""" return str(self._label)
def __eq__(self, other): """Two vertices are equal if they have the same labels.""" if self is other: return True if type(self) != type(other): return False return self.getLabel() == other.getLabel() def __hash__(self): # Derrf Seitz 12/16/2015 """Hash method for a vertex.""" return hash(self._label)
# Methods used by LinkedGraph def addEdgeTo(self, toVertex, weight): """Connects the vertices with an edge.""" edge = LinkedEdge(self, toVertex, weight) self._edgeList.append(edge) def getEdgeTo(self, toVertex): """Returns the connecting edge if it exists, or None otherwise.""" edge = LinkedEdge(self, toVertex) try: return self._edgeList[self._edgeList.index(edge)] except: return None
def incidentEdges(self): """Returns the incident edges of this vertex.""" return iter(self._edgeList) def neighboringVertices(self): """Returns the neighboring vertices of this vertex.""" vertices = list() for edge in self._edgeList: vertices.append(edge.getOtherVertex(self)) return iter(vertices) def removeEdgeTo(self, toVertex): """Returns True if the edge exists and is removed, or False otherwise.""" edge = LinkedEdge(self, toVertex) if edge in self._edgeList: self._edgeList.remove(edge) return True else: return False
class LinkedDirectedGraph(AbstractCollection):
# A graph has a count of vertices, a count of edges, # and a dictionary of label/vertex pairs.
def __init__(self, sourceCollection = None): self._edgeCount = 0 self._vertices = {} AbstractCollection.__init__(self, sourceCollection) # in, ==, +, and clone operations
def __contains__(self, item): """Returns True if item is a label in a vertex, or False otherwise.""" for vertex in self: if vertex.getLabel() == item: return True return False
def clone(self): """Returns an exact copy of self, including vertex labels, edges connecting vertices, and edge weights.""" newGraph = type(self)(map(lambda vertex: vertex.getLabel(), self)) for vertex in self: for neighbor in vertex.neighboringVertices(): edge = vertex.getEdgeTo(neighbor) newGraph.addEdge(vertex.getLabel(), neighbor.getLabel(), edge.getWeight()) return newGraph
def __add__(self, other): """Returns a new collection consisting of the items in self and other. This will be a graph with the contents of the operand graphs as separate components. Precondition: the labels in the two graph operands are disjoint. Raises: AttributeError if the labels in the two operand graphs are not disjoint.""" newGraph = self.clone() for vertex in other: newGraph.add(vertex.getLabel()) for vertex in other: for neighbor in vertex.neighboringVertices(): edge = vertex.getEdgeTo(neighbor) newGraph.addEdge(vertex.getLabel(), neighbor.getLabel(), edge.getWeight()) return newGraph
def __eq__(self, other): """Returns True if self and other are identical, or True if self and other are of the same type, have the same number of vertices, and these vertices are connected in the same manner (including same labels and edge weights).""" # Check simple criteria first if self is other: return True if type(self) != type(other): return False if len(self) != len(other): return False for vertex in self: # All vertex labels must match if not vertex.getLabel() in other: print("Mismatched vertices") return False # Vertices with the same labels must have the same # incident edges otherVertex = other.getVertex(vertex.getLabel()) selfEdges = list(vertex.incidentEdges()) otherEdges = list(otherVertex.incidentEdges()) if len(selfEdges) != len(otherEdges): return False for edge in selfEdges: found = False for otherEdge in otherEdges: if edge == otherEdge and edge.getWeight() == otherEdge.getWeight(): found = True break if not found: return False return True
# Methods for clearing, marks, sizes, string rep
def clear(self): """Clears the graph.""" self._size = 0 self._edgeCount = 0 self._vertices = {}
def clearEdgeMarks(self): """Clears all the edge marks.""" for edge in self.edges(): edge.clearMark() def clearVertexMarks(self): """Clears all the vertex marks.""" for vertex in self.vertices(): vertex.clearMark() def sizeEdges(self): """Returns the number of edges.""" return self._edgeCount def sizeVertices(self): """Returns the number of vertices.""" return len(self) def __str__(self): """Returns the string representation of the graph.""" result = str(len(self)) + " Vertices: " for vertex in self._vertices: result += " " + str(vertex) result += " "; result += str(self.sizeEdges()) + " Edges: " for edge in self.edges(): result += " " + str(edge) return result
def add(self, label): """For compatibility with other collections.""" self.addVertex(label)
# Vertex related methods def addVertex(self, label): """Precondition: a vertex with label must not already be in the graph. Raises: AttibuteError if a vertex with label is already in the graph.""" if self.containsVertex(label): raise AttributeError("Label " + str(label) + " already in graph.""") self._vertices[label] = LinkedVertex(label) self._size += 1 def containsVertex (self, label): return label in self._vertices def getVertex(self, label): """Precondition: a vertex with label must already be in the graph. Raises: AttibuteError if a vertex with label is not already in the graph.""" if not self.containsVertex(label): raise AttributeError("Label " + str(label) + " not in graph.""") return self._vertices[label] def removeVertex(self, label): """Returns True if the vertex was removed, or False otherwise.""" removedVertex = self._vertices.pop(label, None) if removedVertex is None: return False # Examine all other vertices to remove edges # directed at the removed vertex for vertex in self.vertices(): if vertex.removeEdgeTo(removedVertex): self._edgeCount -= 1
# Examine all edges from the removed vertex to others for edge in removedVertex.incidentEdges(): self._edgeCount -= 1 self._size -= 1 return True # Methods related to edges
def addEdge(self, fromLabel, toLabel, weight): """Connects the vertices with an edge with the given weight. Preconditions: vertices with fromLabel and toLabel must already be in the graph. The vertices must not already be connected by an edge. Raises: AttibuteError if the vertices are not already in the graph or they are already connected.""" fromVertex = self.getVertex(fromLabel) toVertex = self.getVertex(toLabel) if self.getEdge(fromLabel, toLabel): raise AttributeError("An edge already connects " + str(fromLabel) + " and " + str(toLabel)) fromVertex.addEdgeTo(toVertex, weight) self._edgeCount += 1 def containsEdge(self, fromLabel, toLabel): """Returns True if an edge connects the vertices, or False otherwise.""" return self.getEdge(fromLabel, toLabel) != None def getEdge(self, fromLabel, toLabel): """Returns the edge connecting the two vertices, or None if no edge exists. Precondition: vertices with fromLabel and toLabel must already be in the graph. Raises: AttibuteError if the vertices are not already in the graph.""" fromVertex = self.getVertex(fromLabel) toVertex = self.getVertex(toLabel) return fromVertex.getEdgeTo(toVertex) def removeEdge (self, fromLabel, toLabel): """Returns True if the edge was removed, or False otherwise. Precondition: vertices with fromLabel and toLabel must already be in the graph. Raises: AttibuteError if the vertices are not already in the graph.""" fromVertex = self.getVertex(fromLabel) toVertex = self.getVertex(toLabel) edgeRemovedFlg = fromVertex.removeEdgeTo(toVertex) if edgeRemovedFlg: self._edgeCount -= 1 return edgeRemovedFlg
# Iterators def __iter__(self): """Supports iteration over a view of self (the vertices).""" return self.vertices()
def edges(self): """Supports iteration over the edges in the graph.""" result = list() for vertex in self.vertices(): result += list(vertex.incidentEdges()) return iter(result) def vertices(self): """Supports iteration over the vertices in the graph.""" return iter(self._vertices.values())
def incidentEdges(self, label): """Supports iteration over the incident edges of the given verrtex. Precondition: a vertex with label must already be in the graph. Raises: AttibuteError if a vertex with label is not already in the graph.""" return self.getVertex(label).incidentEdges() def neighboringVertices(self, label): """Supports iteration over the neighboring vertices of the given verrtex. Precondition: a vertex with label must already be in the graph. Raises: AttibuteError if a vertex with label is not already in the graph.""" return self.getVertex(label).neighboringVertices() ~~~~~~~~~~~~~~~~~
Output
Graph: 8 Vertices: H A B C E D F G 12 Edges: A>C:0 A>D:0 B>D:0 B>F:0 C>D:0 C>E:0 C>H:0 E>G:0 D>F:0 F>G:0 F>H:0 G>H:0
Courses: H A B C E D F G
Prerequisites: A>C:0 A>D:0 B>D:0 B>F:0 C>D:0 C>E:0 C>H:0 E>G:0 D>F:0 F>G:0 F>H:0 G>H:0
One possible order to take the courses: B A C E D F G H Graph: 8 Vertices: H A B C E D F G 12 Edges: A>C:0 A>D:0 B>D:0 B>F:0 C>D:0 C>E:0 C>H:0 E>G:0 D>F:0 F>G:0 F>H:0 G>H:0
Courses: H A B C E D F G
Prerequisites: A>C:0 A>D:0 B>D:0 B>F:0 C>D:0 C>E:0 C>H:0 E>G:0 D>F:0 F>G:0 F>H:0 G>H:0
One possible order to take the courses: B A C E D F G H
Explanation / Answer
# Replace any "<your code>" comments with your own code statement(s)
# to accomplish the specified task.
# Do not change any other code.
# The following files must be in the same folder:
# abstractcollection.py
# graph.py
from graph import LinkedDirectedGraph
# Part 1:
# Complete the following function:
def topologicalSort(graph):
# <your code>
graph = LinkedDirectedGraph()
# The graph represents the following course prerequisites:
# A requires nothing
# B requires nothing
# C requires A
# D requires A, B, and C
# E requires C
# F requires B and D
# G requires E and F
# H requires C, F, and G
# Part 2:
# Add the vertices:
# <your code>
# Part 3:
# Add the edges:
# <your code>
print("Graph:")
print(graph)
print()
print("Courses:")
# Part 4:
# Display each vertex on a separate line:
# <your code>
print()
print("Prerequisites:")
# Part 5:
# Display each edge on a separate line:
# <your code>
print()
print("One possible order to take the courses:")
# Part 6:
# Display the courses in prerequisite (topological) order:
# <your code>
print()
~~~~~~~~~~~~~~~
"""
File: abstractcollection.py
Copyright 2015 by Ken Lambert
"""
class AbstractCollection(object):
"""An abstract collection implementation."""
# Constructor
def __init__(self, sourceCollection = None):
"""Sets the initial state of self, which includes the
contents of sourceCollection, if it's present."""
self._size = 0
self._modCount = 0
if sourceCollection:
for item in sourceCollection:
self.add(item)
# Accessor methods
def isEmpty(self):
"""Returns True if len(self) == 0, or False otherwise."""
return len(self) == 0
def __len__(self):
"""Returns the number of items in self."""
return self._size
def __str__(self):
"""Returns the string representation of self, using []
as delimiters."""
return "[" + ", ".join(map(str, self)) + "]"
def __eq__(self, other):
"""Returns True if self equals other,
or False otherwise.
Compares pairs of items in the two sequences
generated by the iterators on self and other."""
if self is other: return True
if type(self) != type(other) or
len(self) != len(other):
return False
otherIter = iter(other)
for item in self:
if item != next(otherIter):
return False
return True
def __add__(self, other):
"""Returns a new collection containing the contents
of self and other."""
result = type(self)(self)
for item in other:
result.add(item)
return result
def count(self, item):
"""Returns the number of instance of item in self."""
counter = 0
for nextItem in self:
if item == nextItem: counter += 1
return counter
# These methods track and update the modCount, which is used to
# prevent mutations within the context of an iterator (for loop)
def getModCount(self):
"""Returns the number of modifications to the collection."""
return self._modCount
def incModCount(self):
"""Increments the number of modifications to the collection."""
self._modCount += 1
~~~~~~~~~~~~~~~
"""
File: graph.py
Copyright 2015 by Ken Lambert
Adds the in, +, ==, and clone operations to support graph-specific behavior.
"""
from abstractcollection import AbstractCollection
class LinkedEdge(object):
# An edge has a source vertex, a destination vertex,
# a weight, and a mark attribute.
def __init__(self, fromVertex, toVertex, weight = None):
self._vertex1 = fromVertex
self._vertex2 = toVertex
self._weight = weight
self._mark = False
def clearMark(self):
"""Clears the mark on the edge."""
self._mark = False
def __eq__(self, other):
"""Two edges are equal if they connect
the same vertices."""
if self is other: return True
if type(self) != type(other):
return False
return self._vertex1 == other._vertex1 and
self._vertex2 == other._vertex2
def getFromVertex(self): # Derrf Seitz 12/17/2015
"""Returns the edge's origin vertex."""
return self._vertex1
def getOtherVertex(self, thisVertex):
"""Returns the vertex opposite thisVertex."""
if thisVertex == None or thisVertex == self._vertex2:
return self._vertex1
else:
return self._vertex2
def getToVertex(self):
"""Returns the edge's destination vertex."""
return self._vertex2
def getWeight(self):
"""Returns the edge's weight."""
return self._weight
def isMarked(self):
"""Returns True if the edge is marked
or False otherwise."""
return self._mark
def setMark(self):
"""Sets the mark on the edge."""
self._mark = True
def setWeight(self, weight):
"""Sets the weight on the edge to weight."""
self._weight = weight
def __str__(self):
"""Returns the string representation of the edge."""
return str(self._vertex1) + ">" +
str(self._vertex2) + ":" +
str(self._weight)
class LinkedVertex(object):
# A vertex has a label, a list of incident edges,
# and a mark attribute.
def __init__(self, label):
self._label = label
self._edgeList = list()
self._mark = False
def clearMark(self):
"""Clears the mark on the vertex."""
self._mark = False;
def getLabel(self):
"""Returns the label of the vertex."""
return self._label
def isMarked(self):
"""Returns True if the vertex is marked
or False otherwise."""
return self._mark
def setLabel(self, label, g):
"""Sets the vertex's label to label."""
g._vertices.pop(self._label, None)
g._vertices[label] = self
self._label = label
def setMark(self):
"""Sets the mark on the vertex."""
self._mark = True
def __str__(self):
"""Returns the string representation of the vertex."""
return str(self._label)
def __eq__(self, other):
"""Two vertices are equal if they have
the same labels."""
if self is other: return True
if type(self) != type(other): return False
return self.getLabel() == other.getLabel()
def __hash__(self): # Derrf Seitz 12/16/2015
"""Hash method for a vertex."""
return hash(self._label)
# Methods used by LinkedGraph
def addEdgeTo(self, toVertex, weight):
"""Connects the vertices with an edge."""
edge = LinkedEdge(self, toVertex, weight)
self._edgeList.append(edge)
def getEdgeTo(self, toVertex):
"""Returns the connecting edge if it exists, or
None otherwise."""
edge = LinkedEdge(self, toVertex)
try:
return self._edgeList[self._edgeList.index(edge)]
except:
return None
def incidentEdges(self):
"""Returns the incident edges of this vertex."""
return iter(self._edgeList)
def neighboringVertices(self):
"""Returns the neighboring vertices of this vertex."""
vertices = list()
for edge in self._edgeList:
vertices.append(edge.getOtherVertex(self))
return iter(vertices)
def removeEdgeTo(self, toVertex):
"""Returns True if the edge exists and is removed,
or False otherwise."""
edge = LinkedEdge(self, toVertex)
if edge in self._edgeList:
self._edgeList.remove(edge)
return True
else:
return False
class LinkedDirectedGraph(AbstractCollection):
# A graph has a count of vertices, a count of edges,
# and a dictionary of label/vertex pairs.
def __init__(self, sourceCollection = None):
self._edgeCount = 0
self._vertices = {}
AbstractCollection.__init__(self, sourceCollection)
# in, ==, +, and clone operations
def __contains__(self, item):
"""Returns True if item is a label in a vertex,
or False otherwise."""
for vertex in self:
if vertex.getLabel() == item:
return True
return False
def clone(self):
"""Returns an exact copy of self, including vertex labels,
edges connecting vertices, and edge weights."""
newGraph = type(self)(map(lambda vertex: vertex.getLabel(), self))
for vertex in self:
for neighbor in vertex.neighboringVertices():
edge = vertex.getEdgeTo(neighbor)
newGraph.addEdge(vertex.getLabel(),
neighbor.getLabel(),
edge.getWeight())
return newGraph
def __add__(self, other):
"""Returns a new collection consisting of the
items in self and other. This will be a graph with
the contents of the operand graphs as separate components.
Precondition: the labels in the two graph operands are disjoint.
Raises: AttributeError if the labels in the two operand graphs
are not disjoint."""
newGraph = self.clone()
for vertex in other:
newGraph.add(vertex.getLabel())
for vertex in other:
for neighbor in vertex.neighboringVertices():
edge = vertex.getEdgeTo(neighbor)
newGraph.addEdge(vertex.getLabel(),
neighbor.getLabel(),
edge.getWeight())
return newGraph
def __eq__(self, other):
"""Returns True if self and other are identical, or
True if self and other are of the same type, have the
same number of vertices, and these vertices are connected
in the same manner (including same labels and edge weights)."""
# Check simple criteria first
if self is other: return True
if type(self) != type(other): return False
if len(self) != len(other): return False
for vertex in self:
# All vertex labels must match
if not vertex.getLabel() in other:
print("Mismatched vertices")
return False
# Vertices with the same labels must have the same
# incident edges
otherVertex = other.getVertex(vertex.getLabel())
selfEdges = list(vertex.incidentEdges())
otherEdges = list(otherVertex.incidentEdges())
if len(selfEdges) != len(otherEdges):
return False
for edge in selfEdges:
found = False
for otherEdge in otherEdges:
if edge == otherEdge and
edge.getWeight() == otherEdge.getWeight():
found = True
break
if not found: return False
return True
# Methods for clearing, marks, sizes, string rep
def clear(self):
"""Clears the graph."""
self._size = 0
self._edgeCount = 0
self._vertices = {}
def clearEdgeMarks(self):
"""Clears all the edge marks."""
for edge in self.edges():
edge.clearMark()
def clearVertexMarks(self):
"""Clears all the vertex marks."""
for vertex in self.vertices():
vertex.clearMark()
def sizeEdges(self):
"""Returns the number of edges."""
return self._edgeCount
def sizeVertices(self):
"""Returns the number of vertices."""
return len(self)
def __str__(self):
"""Returns the string representation of the graph."""
result = str(len(self)) + " Vertices: "
for vertex in self._vertices:
result += " " + str(vertex)
result += " ";
result += str(self.sizeEdges()) + " Edges: "
for edge in self.edges():
result += " " + str(edge)
return result
def add(self, label):
"""For compatibility with other collections."""
self.addVertex(label)
# Vertex related methods
def addVertex(self, label):
"""Precondition: a vertex with label must not
already be in the graph.
Raises: AttibuteError if a vertex with label
is already in the graph."""
if self.containsVertex(label):
raise AttributeError("Label " + str(label) + " already in graph.""")
self._vertices[label] = LinkedVertex(label)
self._size += 1
def containsVertex (self, label):
return label in self._vertices
def getVertex(self, label):
"""Precondition: a vertex with label must already be in the graph.
Raises: AttibuteError if a vertex with label is not already in the graph."""
if not self.containsVertex(label):
raise AttributeError("Label " + str(label) + " not in graph.""")
return self._vertices[label]
def removeVertex(self, label):
"""Returns True if the vertex was removed, or False otherwise."""
removedVertex = self._vertices.pop(label, None)
if removedVertex is None:
return False
# Examine all other vertices to remove edges
# directed at the removed vertex
for vertex in self.vertices():
if vertex.removeEdgeTo(removedVertex):
self._edgeCount -= 1
# Examine all edges from the removed vertex to others
for edge in removedVertex.incidentEdges():
self._edgeCount -= 1
self._size -= 1
return True
# Methods related to edges
def addEdge(self, fromLabel, toLabel, weight):
"""Connects the vertices with an edge with the given weight.
Preconditions: vertices with fromLabel and toLabel must
already be in the graph.
The vertices must not already be connected by an edge.
Raises: AttibuteError if the vertices
are not already in the graph or they are already connected."""
fromVertex = self.getVertex(fromLabel)
toVertex = self.getVertex(toLabel)
if self.getEdge(fromLabel, toLabel):
raise AttributeError("An edge already connects " +
str(fromLabel) + " and " +
str(toLabel))
fromVertex.addEdgeTo(toVertex, weight)
self._edgeCount += 1
def containsEdge(self, fromLabel, toLabel):
"""Returns True if an edge connects the vertices,
or False otherwise."""
return self.getEdge(fromLabel, toLabel) != None
def getEdge(self, fromLabel, toLabel):
"""Returns the edge connecting the two vertices, or None if
no edge exists.
Precondition: vertices with fromLabel and toLabel must
already be in the graph.
Raises: AttibuteError if the vertices
are not already in the graph."""
fromVertex = self.getVertex(fromLabel)
toVertex = self.getVertex(toLabel)
return fromVertex.getEdgeTo(toVertex)
def removeEdge (self, fromLabel, toLabel):
"""Returns True if the edge was removed, or False otherwise.
Precondition: vertices with fromLabel and toLabel must
already be in the graph.
Raises: AttibuteError if the vertices
are not already in the graph."""
fromVertex = self.getVertex(fromLabel)
toVertex = self.getVertex(toLabel)
edgeRemovedFlg = fromVertex.removeEdgeTo(toVertex)
if edgeRemovedFlg:
self._edgeCount -= 1
return edgeRemovedFlg
# Iterators
def __iter__(self):
"""Supports iteration over a view of self (the vertices)."""
return self.vertices()
def edges(self):
"""Supports iteration over the edges in the graph."""
result = list()
for vertex in self.vertices():
result += list(vertex.incidentEdges())
return iter(result)
def vertices(self):
"""Supports iteration over the vertices in the graph."""
return iter(self._vertices.values())
def incidentEdges(self, label):
"""Supports iteration over the incident edges of the
given verrtex.
Precondition: a vertex with label must already be in the graph.
Raises: AttibuteError if a vertex with label is not already in the graph."""
return self.getVertex(label).incidentEdges()
def neighboringVertices(self, label):
"""Supports iteration over the neighboring vertices of the
given verrtex.
Precondition: a vertex with label must already be in the graph.
Raises: AttibuteError if a vertex with label is not already in the graph."""
return self.getVertex(label).neighboringVertices()
Related Questions
drjack9650@gmail.com
Navigate
Integrity-first tutoring: explanations and feedback only — we do not complete graded work. Learn more.