1+ class Vertex :
2+ """Lighweight vertex structure for a graph"""
3+ __slots__ = '_element'
4+
5+ def __init__ (self , x ):
6+ """Should not call the constructor directly, instead we should use the insert_vertex(x) method
7+ to create the vertex object"""
8+ self ._element = x
9+
10+ def element (self ):
11+ """return element associated with thi vertex"""
12+ return self ._element
13+
14+ def __hash__ (self ): # will allow vertex to be a map/set key
15+ return hash (id (self ))
16+
17+ class Edge :
18+ """Lighweight edge structure for a graph"""
19+
20+ __slots__ = '_origin' , '_destination' , '_element'
21+
22+ def __init__ (self , u , v , x ):
23+ """Should not call constructor directly use, insert_edge(u , v ,x) method from graph class to create
24+ edge object"""
25+ self ._origin = u
26+ self ._destination = v
27+ self ._element = x
28+
29+ def element (self ):
30+ """return element associated with the edge"""
31+ return self ._element
32+
33+ def endpoints (self ):
34+ """return the vertices (U,V) that are endpoints for this edge in a tuple form """
35+ return (self ._origin , self ._destination )
36+
37+ def opposite (self , v ):
38+ """Return the vertex opposite to v in this edge """
39+
40+ if v is self ._origin :
41+ return self ._destination
42+ else :
43+ return self ._origin
44+
45+ def __hash__ (self ): # will allow wdge to be a map/set key
46+ return hash ((self ._origin , self ._destination ))
47+
48+ class Graph :
49+ """Representation of a simple grahph using an adjacency map """
50+
51+ def __init__ (self , directed = False ):
52+ """Create an empty graph, which is undirected by default
53+
54+ Graph is directed if we parameter driected is set to True in constructor call"""
55+
56+ self ._outgoing = {} # map that will contain incident edges of vertex v
57+ # if map is directed we need an extra *in incident* map
58+ self ._incoming = {} if directed else self ._outgoing
59+
60+ def is_directed (self ):
61+ """Return true if the graph is a directed grph False otherwise
62+
63+ Property based on the original declaration of the graph, not its contents"""
64+ return self ._incoming is not self ._outgoing
65+
66+ def vertex_count (self ):
67+ """Return the number of vertices in the graph"""
68+ return len (self ._outgoing )
69+
70+ def vertices (self ):
71+ #return an iteration of vertices in the graph which are the keys to the incident map of outgoing endpoints
72+ return self ._outgoing .keys ()
73+
74+ def edge_count (self ):
75+ """Return the number of edges in the graph"""
76+ total = sum (len (self ._outgoing [v ]) for v in self ._outgoing ) # len of values for each key in incident map
77+ # for undirected graphs, do not double-count the edges
78+ return total if self .is_directed else total // 2
79+
80+ def edges (self ):
81+ """return a set of all edges of the graph"""
82+ result = set () # to avoid double-reporting edges of undirected graphs
83+ for secondary_map in self ._outgoing .values ():
84+ result .update (secondary_map .values ) #Update a set with the union of itself and others.
85+
86+ return result
87+
88+ def get_edge (self , u , v ):
89+ """return the edge from u to v, or None if not adjacent"""
90+ return self ._outgoing [u ].get (v ) #return None if v not adjacent
91+
92+ def degree (self , v , outgoing = True ):
93+ """Return the number of (outgoing) edges incident to vertex v in the graph
94+
95+ If graph is directed optional parameter used to count incoming edges """
96+
97+ adj = self ._outgoing if outgoing else self ._incoming
98+
99+ return len (adj [v ])
100+
101+ def incident_edges (self , v , outgoing = True ):
102+ """Return all (outgoing) edges incident to vertex v in the graph
103+
104+ If graph is directed, optional parameter used to request incoming edges"""
105+
106+ adj = self ._outgoing if outgoing else self ._incoming
107+ for edge in adj [v ].values ():
108+ yield edge
109+
110+ def insert_vertex (self , x = None ):
111+ """Insert and return a new Vertex with element x"""
112+
113+ vertex = self .Vertex (x )
114+ self ._outgoing [vertex ] = {} # new vertex will contain empty map of icidency
115+ if self .is_directed ():
116+ self ._incoming [v ] = {} # if directed graph 2 incidency maps are needed.
117+
118+ return vertex
119+
120+ def insert_edge (self , u , v , x = None ):
121+ """Inser and return a new Edge from u to v with auxiliary element x"""
122+ e = self .Edge (u , v , x )
123+ self ._outgoing [u ][v ] = e
124+ self .incident_edges [v ][u ] = e
0 commit comments