class SymbolGraph: """The SymbolGraph class represents an undirected graph, where the vertex names are arbitrary strings. By providing mappings between vertex names and integers, it serves as a wrapper around the Graph data type, which assumes the vertex names are integers between 0 and V - 1. """ def __init__(self, vertices): self._st = BinarySearchST() # string -> index # built symbol table to translate strings into indices for i in range(len(vertices)): self._st.put(vertices[i], i) # built array to translate from indices to strings self._keys = [None] * self._st.size() # index -> string for name in self._st.keys(): self._keys[self._st.get(name)] = name # initialise graph self._graph = Graph(self._st.size()) # the underlying graph def V(self): return self._graph._V def E(self): return self._graph._E def size(self): return self._graph._V def __len__(self): return self._graph._V def contains(self, s): return self._st.contains(s) def index_of(self, s): return self._st.get(s) def name_of(self, v): return self._keys[v] def add_edge(self, u, v): self._graph.add_edge(self.index_of(u), self.index_of(v)) def adj(self, u): return [self.name_of(adj) for adj in self._graph.adj(self.index_of(u))] def degree(self, u): return self._graph.degree(self.index_of(u)) def graph(self): return self._graph
def __init__(self, vertices): self._st = BinarySearchST() # string -> index # built symbol table to translate strings into indices for i in range(len(vertices)): self._st.put(vertices[i], i) # built array to translate from indices to strings self._keys = [None] * self._st.size() # index -> string for name in self._st.keys(): self._keys[self._st.get(name)] = name # initialise graph self._graph = Graph(self._st.size()) # the underlying graph
def from_graph(G): """ Initializes a new graph that is a deep copy of G :param G: the graph to copy :returns: copy of G """ g = Graph(G.V()) g._E = G.E() for v in range(G.V()): # reverse so that adjacency list is in same order as original reverse = Stack() for w in G._adj[v]: reverse.push(w) for w in reverse: g._adj[v].add(w)
def __init__(self, filename, delimiter): """Initializes a graph from a file using the specified delimiter. Each line in the file contains the name of a vertex, followed by a list of the names of the vertices adjacent to that vertex, separated by the delimiter. :param filename: the name of the file :param delimiter: the delimiter between fields """ self._st = BinarySearchST() # string -> index # First pass builds the index by reading strings to associate # distinct strings with an index stream = InStream(filename) while not stream.isEmpty(): a = stream.readLine().split(delimiter) for i in range(len(a)): if not self._st.contains(a[i]): self._st.put(a[i], self._st.size()) stdio.writef("Done reading %s\n", filename) # inverted index to get keys in an array self._keys = [None] * self._st.size() # index -> string for name in self._st.keys(): self._keys[self._st.get(name)] = name # second pass builds the graph by connecting first vertex on each # line to all others self._graph = Graph(self._st.size()) # the underlying graph stream = InStream(filename) while stream.hasNextLine(): a = stream.readLine().split(delimiter) v = self._st.get(a[0]) for i in range(1, len(a)): w = self._st.get(a[i]) self._graph.add_edge(v, w)
class SymbolGraph: """The SymbolGraph class represents an undirected graph, where the vertex names are arbitrary strings. By providing mappings between vertex names and integers, it serves as a wrapper around the Graph data type, which assumes the vertex names are integers. between 0 and V - 1. It also supports initializing a symbol graph from a file. This implementation uses an ST to map from strings to integers, an array to map from integers to strings, and a Graph to store the underlying graph. The index_of and contains operations take time proportional to log V, where V is the number of vertices. The name_of operation takes constant time. """ def __init__(self, filename, delimiter): """Initializes a graph from a file using the specified delimiter. Each line in the file contains the name of a vertex, followed by a list of the names of the vertices adjacent to that vertex, separated by the delimiter. :param filename: the name of the file :param delimiter: the delimiter between fields """ self._st = BinarySearchST() # string -> index # First pass builds the index by reading strings to associate # distinct strings with an index stream = InStream(filename) while not stream.isEmpty(): a = stream.readLine().split(delimiter) for i in range(len(a)): if not self._st.contains(a[i]): self._st.put(a[i], self._st.size()) stdio.writef("Done reading %s\n", filename) # inverted index to get keys in an array self._keys = [None] * self._st.size() # index -> string for name in self._st.keys(): self._keys[self._st.get(name)] = name # second pass builds the graph by connecting first vertex on each # line to all others self._graph = Graph(self._st.size()) # the underlying graph stream = InStream(filename) while stream.hasNextLine(): a = stream.readLine().split(delimiter) v = self._st.get(a[0]) for i in range(1, len(a)): w = self._st.get(a[i]) self._graph.add_edge(v, w) def contains(self, s): """Does the graph contain the vertex named s? :param s: the name of a vertex :return:s true if s is the name of a vertex, and false otherwise """ return self._st.contains(s) def index_of(self, s): """Returns the integer associated with the vertex named s. :param s: the name of a vertex :returns: the integer (between 0 and V - 1) associated with the vertex named s """ return self._st.get(s) def name_of(self, v): """Returns the name of the vertex associated with the integer v. @param v the integer corresponding to a vertex (between 0 and V - 1) @throws IllegalArgumentException unless 0 <= v < V @return the name of the vertex associated with the integer v """ self._validateVertex(v) return self._keys[v] def graph(self): return self._graph def _validateVertex(self, v): # throw an IllegalArgumentException unless 0 <= v < V V = self._graph.V() if v < 0 or v >= V: raise ValueError("vertex {} is not between 0 and {}".format(v, V - 1))
if not self._marked[s]: self.dfs(-1, s) def dfs(self, u, v): self._marked[v] = True for adj in self._G.adj(v): if not self._marked[adj]: self.dfs(v, adj) elif u != adj: self._has_cycle = True def has_cycle(self): return self._has_cycle if __name__ == '__main__': G = Graph(7) G.add_edge(0, 1) G.add_edge(0, 2) G.add_edge(0, 5) G.add_edge(0, 6) G.add_edge(3, 4) G.add_edge(3, 5) G.add_edge(4, 5) G.add_edge(4, 6) G.add_edge(1, 3) print(G) cycle_detector = Cycles(G) print(cycle_detector.has_cycle())
return path def _validateVertex(self, v): # throw an ValueError unless 0 <= v < V V = len(self._marked) if v < 0 or v >= V: raise ValueError("vertex {} is not between 0 and {}".format( v, V - 1)) if __name__ == "__main__": from itu.algs4.stdlib import stdio from itu.algs4.graphs.graph import Graph from itu.algs4.stdlib.instream import InStream import sys In = InStream(sys.argv[1]) G = Graph.from_stream(In) s = int(sys.argv[2]) dfs = DepthFirstPaths(G, s) for v in range(G.V()): if dfs.has_path_to(v): stdio.writef("%d to %d: ", s, v) for x in dfs.path_to(v): if x == s: stdio.write(x) else: stdio.writef("-%i", x) stdio.writeln() else: stdio.writef("%d to %d: not connected\n", s, v)
path = Stack() curr = v while curr != self._s: path.push(curr) curr = self._edgeto[curr] path.push(self._s) return path def connected(self): return self._count == self._G.V() # client code if __name__ == '__main__': # initialise example graph G = Graph(7) G.add_edge(0, 1) G.add_edge(0, 2) G.add_edge(0, 5) G.add_edge(0, 6) G.add_edge(3, 4) G.add_edge(3, 5) G.add_edge(4, 5) G.add_edge(4, 6) print(G) paths = Paths(G, s=0) print(paths._marked) print(paths._edgeto) print(f"Is Connected: {paths.connected()}") for i in range(G.V()):
# solution to wiener index in may 2018 dsa exam from itu.algs4.graphs.graph import Graph from itu.algs4.fundamentals.queue import Queue from itertools import combinations G = Graph(7) G.add_edge(0, 1) G.add_edge(0, 2) G.add_edge(1, 3) G.add_edge(1, 4) G.add_edge(2, 5) G.add_edge(2, 6) def shortest_path(G, u, v): marked = [False] * G.V() edge_to = [None] * G.V() q = Queue() marked[u] = True q.enqueue(u) while not q.is_empty(): s = q.dequeue() for adj in G.adj(s): if not marked[adj]: marked[s] = True edge_to[adj] = s q.enqueue(adj) dist = 0
marked[adj] = True edge_to[adj] = v q.enqueue(adj) # compute dist of all paths paths = [] for i in range(G.V()): dist = 0 curr = i while edge_to[curr] != None: dist += 1 curr = edge_to[curr] paths.append(dist) return max(paths) G = Graph(7) G.add_edge(0, 1) G.add_edge(0, 2) G.add_edge(1, 3) G.add_edge(2, 3) G.add_edge(2, 4) G.add_edge(3, 4) G.add_edge(5, 4) G.add_edge(5, 6) G.add_edge(3, 6) print(diameter(G))
for adj in self._G.adj(s): if not self._marked[adj]: self._coloring[adj] = not self._coloring[s] self.dfs(adj) elif self._coloring[s] == self._coloring[adj]: self._is_bipartite = False def is_bipartite(self): return self._is_bipartite def coloring(self): if not self.is_bipartite(): raise ValueError('No two-coloring possible') return [ 'Red' if self._coloring[i] else 'Blue' for i in range(len(self._coloring)) ] if __name__ == '__main__': G = Graph(4) G.add_edge(0, 1) G.add_edge(0, 2) G.add_edge(0, 3) print(G) colorer = Bipartite(G) print(colorer.coloring()) print(colorer.is_bipartite())
return sum(self.matrix[u]) def average_degree(self): return 2 * (self._E / self._V) def number_self_loops(self): num = 0 for i in range(self._V): if self.matrix[i][i] == 1: num += 1 return num def __str__(self): string = '[' + str(self.matrix[0]) + '\n' for i in range(1, self._V - 1): string += f' {self.matrix[i]}\n' return string + f' {self.matrix[-1]}]' if __name__ == '__main__': graph = Graph(5) graph.add_edge(0, 1) graph.add_edge(0, 3) graph.add_edge(2, 3) graph.add_edge(0, 0) print(graph.degree(0)) print(graph.adj(0)) # print(graph.number_self_loops()) # print(graph.average_degree()) print(graph)