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