|  | 
|  | 1 | +# Copyright 2013, Michael H. Goldwasser | 
|  | 2 | +# | 
|  | 3 | +# Developed for use with the book: | 
|  | 4 | +# | 
|  | 5 | +#    Data Structures and Algorithms in Python | 
|  | 6 | +#    Michael T. Goodrich, Roberto Tamassia, and Michael H. Goldwasser | 
|  | 7 | +#    John Wiley & Sons, 2013 | 
|  | 8 | +# | 
|  | 9 | +# This program is free software: you can redistribute it and/or modify | 
|  | 10 | +# it under the terms of the GNU General Public License as published by | 
|  | 11 | +# the Free Software Foundation, either version 3 of the License, or | 
|  | 12 | +# (at your option) any later version. | 
|  | 13 | +# | 
|  | 14 | +# This program is distributed in the hope that it will be useful, | 
|  | 15 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | 16 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|  | 17 | +# GNU General Public License for more details. | 
|  | 18 | +# | 
|  | 19 | +# You should have received a copy of the GNU General Public License | 
|  | 20 | +# along with this program.  If not, see <http://www.gnu.org/licenses/>. | 
|  | 21 | + | 
|  | 22 | +class Graph: | 
|  | 23 | +  """Representation of a simple graph using an adjacency map.""" | 
|  | 24 | + | 
|  | 25 | +  #------------------------- nested Vertex class ------------------------- | 
|  | 26 | +  class Vertex: | 
|  | 27 | +    """Lightweight vertex structure for a graph.""" | 
|  | 28 | +    __slots__ = '_element' | 
|  | 29 | +   | 
|  | 30 | +    def __init__(self, x): | 
|  | 31 | +      """Do not call constructor directly. Use Graph's insert_vertex(x).""" | 
|  | 32 | +      self._element = x | 
|  | 33 | +   | 
|  | 34 | +    def element(self): | 
|  | 35 | +      """Return element associated with this vertex.""" | 
|  | 36 | +      return self._element | 
|  | 37 | +   | 
|  | 38 | +    def __hash__(self):         # will allow vertex to be a map/set key | 
|  | 39 | +      return hash(id(self)) | 
|  | 40 | + | 
|  | 41 | +    def __str__(self): | 
|  | 42 | +      return str(self._element) | 
|  | 43 | +     | 
|  | 44 | +  #------------------------- nested Edge class ------------------------- | 
|  | 45 | +  class Edge: | 
|  | 46 | +    """Lightweight edge structure for a graph.""" | 
|  | 47 | +    __slots__ = '_origin', '_destination', '_element' | 
|  | 48 | +   | 
|  | 49 | +    def __init__(self, u, v, x): | 
|  | 50 | +      """Do not call constructor directly. Use Graph's insert_edge(u,v,x).""" | 
|  | 51 | +      self._origin = u | 
|  | 52 | +      self._destination = v | 
|  | 53 | +      self._element = x | 
|  | 54 | +   | 
|  | 55 | +    def endpoints(self): | 
|  | 56 | +      """Return (u,v) tuple for vertices u and v.""" | 
|  | 57 | +      return (self._origin, self._destination) | 
|  | 58 | +   | 
|  | 59 | +    def opposite(self, v): | 
|  | 60 | +      """Return the vertex that is opposite v on this edge.""" | 
|  | 61 | +      if not isinstance(v, Graph.Vertex): | 
|  | 62 | +        raise TypeError('v must be a Vertex') | 
|  | 63 | +      return self._destination if v is self._origin else self._origin | 
|  | 64 | +      raise ValueError('v not incident to edge') | 
|  | 65 | +   | 
|  | 66 | +    def element(self): | 
|  | 67 | +      """Return element associated with this edge.""" | 
|  | 68 | +      return self._element | 
|  | 69 | +   | 
|  | 70 | +    def __hash__(self):         # will allow edge to be a map/set key | 
|  | 71 | +      return hash( (self._origin, self._destination) ) | 
|  | 72 | + | 
|  | 73 | +    def __str__(self): | 
|  | 74 | +      return '({0},{1},{2})'.format(self._origin,self._destination,self._element) | 
|  | 75 | +     | 
|  | 76 | +  #------------------------- Graph methods ------------------------- | 
|  | 77 | +  def __init__(self, directed=False): | 
|  | 78 | +    """Create an empty graph (undirected, by default). | 
|  | 79 | +
 | 
|  | 80 | +    Graph is directed if optional paramter is set to True. | 
|  | 81 | +    """ | 
|  | 82 | +    self._outgoing = {} | 
|  | 83 | +    # only create second map for directed graph; use alias for undirected | 
|  | 84 | +    self._incoming = {} if directed else self._outgoing | 
|  | 85 | + | 
|  | 86 | +  def _validate_vertex(self, v): | 
|  | 87 | +    """Verify that v is a Vertex of this graph.""" | 
|  | 88 | +    if not isinstance(v, self.Vertex): | 
|  | 89 | +      raise TypeError('Vertex expected') | 
|  | 90 | +    if v not in self._outgoing: | 
|  | 91 | +      raise ValueError('Vertex does not belong to this graph.') | 
|  | 92 | +     | 
|  | 93 | +  def is_directed(self): | 
|  | 94 | +    """Return True if this is a directed graph; False if undirected. | 
|  | 95 | +
 | 
|  | 96 | +    Property is based on the original declaration of the graph, not its contents. | 
|  | 97 | +    """ | 
|  | 98 | +    return self._incoming is not self._outgoing # directed if maps are distinct | 
|  | 99 | + | 
|  | 100 | +  def vertex_count(self): | 
|  | 101 | +    """Return the number of vertices in the graph.""" | 
|  | 102 | +    return len(self._outgoing) | 
|  | 103 | + | 
|  | 104 | +  def vertices(self): | 
|  | 105 | +    """Return an iteration of all vertices of the graph.""" | 
|  | 106 | +    return self._outgoing.keys() | 
|  | 107 | + | 
|  | 108 | +  def edge_count(self): | 
|  | 109 | +    """Return the number of edges in the graph.""" | 
|  | 110 | +    total = sum(len(self._outgoing[v]) for v in self._outgoing) | 
|  | 111 | +    # for undirected graphs, make sure not to double-count edges | 
|  | 112 | +    return total if self.is_directed() else total // 2 | 
|  | 113 | + | 
|  | 114 | +  def edges(self): | 
|  | 115 | +    """Return a set of all edges of the graph.""" | 
|  | 116 | +    result = set()       # avoid double-reporting edges of undirected graph | 
|  | 117 | +    for secondary_map in self._outgoing.values(): | 
|  | 118 | +      result.update(secondary_map.values())    # add edges to resulting set | 
|  | 119 | +    return result | 
|  | 120 | + | 
|  | 121 | +  def get_edge(self, u, v): | 
|  | 122 | +    """Return the edge from u to v, or None if not adjacent.""" | 
|  | 123 | +    self._validate_vertex(u) | 
|  | 124 | +    self._validate_vertex(v) | 
|  | 125 | +    return self._outgoing[u].get(v)        # returns None if v not adjacent | 
|  | 126 | + | 
|  | 127 | +  def degree(self, v, outgoing=True):    | 
|  | 128 | +    """Return number of (outgoing) edges incident to vertex v in the graph. | 
|  | 129 | +
 | 
|  | 130 | +    If graph is directed, optional parameter used to count incoming edges. | 
|  | 131 | +    """ | 
|  | 132 | +    self._validate_vertex(v) | 
|  | 133 | +    adj = self._outgoing if outgoing else self._incoming | 
|  | 134 | +    return len(adj[v]) | 
|  | 135 | + | 
|  | 136 | +  def incident_edges(self, v, outgoing=True):    | 
|  | 137 | +    """Return all (outgoing) edges incident to vertex v in the graph. | 
|  | 138 | +
 | 
|  | 139 | +    If graph is directed, optional parameter used to request incoming edges. | 
|  | 140 | +    """ | 
|  | 141 | +    self._validate_vertex(v) | 
|  | 142 | +    adj = self._outgoing if outgoing else self._incoming | 
|  | 143 | +    for edge in adj[v].values(): | 
|  | 144 | +      yield edge | 
|  | 145 | + | 
|  | 146 | +  def insert_vertex(self, x=None): | 
|  | 147 | +    """Insert and return a new Vertex with element x.""" | 
|  | 148 | +    v = self.Vertex(x) | 
|  | 149 | +    self._outgoing[v] = {} | 
|  | 150 | +    if self.is_directed(): | 
|  | 151 | +      self._incoming[v] = {}        # need distinct map for incoming edges | 
|  | 152 | +    return v | 
|  | 153 | +       | 
|  | 154 | +  def insert_edge(self, u, v, x=None): | 
|  | 155 | +    """Insert and return a new Edge from u to v with auxiliary element x. | 
|  | 156 | +
 | 
|  | 157 | +    Raise a ValueError if u and v are not vertices of the graph. | 
|  | 158 | +    Raise a ValueError if u and v are already adjacent. | 
|  | 159 | +    """ | 
|  | 160 | +    if self.get_edge(u, v) is not None:      # includes error checking | 
|  | 161 | +      raise ValueError('u and v are already adjacent') | 
|  | 162 | +    e = self.Edge(u, v, x) | 
|  | 163 | +    self._outgoing[u][v] = e | 
|  | 164 | +    self._incoming[v][u] = e | 
0 commit comments