Module dsa.graph
Module containing graph classes.
Functions
def find_path(graph, start: str, end: str, debug=False)
-
Return the shortest path of two vertices using Dijkstra's Algorithm.
Args
graph
- graph object (adjacency list)
start
- starting vertex
end
- ending vertex
debug
- if True, display the weight table
Returns: A list of vertices that form a shortest path
def shortest_path(graph, start: str, end: str, debug=False)
-
Helper function that returns a weight table and a previous vertex table using Dijkstra's Algorithm.
Args
graph
- adjacency list graph
start
- starting vertex
end
- ending vertex
debug
- if True, display weight table as it is being built
Returns: a tuple of a weight table dictionary and a previous path dictionary
Classes
class AdjacencyListGraph
-
A unweighted adjacency list vertex implementation (allows either directed or undirected representation) vertex labels are string types
Expand source code
class AdjacencyListGraph: """ A unweighted adjacency list vertex implementation (allows either directed or undirected representation) vertex labels are string types """ def __init__(self): #: hash table of vertices in graph self._adjacents = {} def add_adjacent_vertex(self, start_label: str, end_label: str): """ Add an undirected vertex to the adjacency list (same as add_edge()). Args: start_label: starting vertex label end_label: ending vertex label """ self.add_directed_adjacent_vertex(start_label, end_label) if end_label not in self._adjacents: self._adjacents[end_label] = [start_label] else: if start_label not in self._adjacents[end_label]: self._adjacents[end_label].append(start_label) def add_edge(self, start_label: str, end_label: str): """ Add an undirected vertex to the adjacency list (same as add_adjacent_vertex()). Args: start_label: starting vertex label end_label: ending vertex label """ self.add_adjacent_vertex(start_label, end_label) def add_directed_edge(self, start_label: str, end_label: str): """ Add a directed vertex to the adjacency list (same as add_directed_adjacent_vertex()). Args: start_label: starting vertex label end_label: ending vertex label """ self.add_directed_adjacent_vertex(start_label, end_label) def add_directed_adjacent_vertex(self, start_label: str, end_label: str): """ Add a directed vertex to the adjacency list (same as add_directed_edge()). Args: start_label: starting vertex label end_label: ending vertex label """ if start_label not in self._adjacents: self._adjacents[start_label] = [end_label] else: if end_label not in self._adjacents[start_label]: self._adjacents[start_label].append(end_label) if end_label not in self._adjacents: self._adjacents[end_label] = [] def vertices(self) -> list: """" return a list of vertex labels of the graph """ return list(self._adjacents.keys()) def adjacents(self, vertex: str) -> list: """ Return a list of adjacents of a given vertex Args: vertex: starting vertex label """ return self._adjacents[vertex] def df_traverse(self, start_label: str): """ Return a list of vertices through depth first traversal starting at a given vertex Args: start_label: starting vertex label Returns: a list of vertex labels """ return self._df_rec_traverse(start_label, set(), []) def _df_rec_traverse(self, start_label: str, visited, dflist): """ helper depth first traversal recursive function """ visited.add(start_label) dflist.append(start_label) for v in self[start_label]: if v not in visited: self._df_rec_traverse(v, visited, dflist) return dflist def bf_traverse(self, start_label: str): """ Return a list of vertices through breadth first traversal starting at a given vertex Args: start_label: starting vertex label Returns: a list of vertex labels """ visited = set() q = Queue() bflist = [] q.enqueue(start_label) visited.add(start_label) bflist.append(start_label) while len(q) > 0: current = q.dequeue() for v in self[current]: if v not in visited: visited.add(v) q.enqueue(v) bflist.append(v) return bflist def dfs(self, start_label:str, end_label:str) -> str: """ Recursive depth first search. Args: start: beginning vertex end: vertex to search for Returns: vertex in the graph if found, None otherwise. """ def dfs_rec(current: str, end: str, visited): if current == end: return current visited.add(current) for v in self.adjacents(current): if v not in visited: result = dfs_rec(v, end, visited) if result is not None: return result return None return dfs_rec(start_label, end_label, set()) def bfs(self, start_label: str, end_label: str) -> str: """ breadth first search. Args: start_label: beginning vertex end_label: vertex to search for Returns: vertex in the graph if found, None otherwise. """ visited = set() queue = Queue() visited.add(start_label) queue.enqueue(start_label) while len(queue) > 0: current = queue.dequeue() if current == end_label: return current for v in self[current]: if v not in visited: visited.add(v) queue.enqueue(v) return None def __getitem__(self, label: str): """ Args: vertex: vertex label Returns: a list of adjacent vertex labels """ return self._adjacents[label] def is_edge(self, start_label: str, end_label: str) -> bool: """ Return boolean if an edge exists Args: start_label: starting vertex label end_label: starting vertex label Returns: boolean of whether there is an edge from start to end """ return end_label in self[start_label] def __len__(self): return len(self._adjacents) def edges(self): """ Return a list of edges in the graph. Each edge is represented by a tuple (start, end) """ edges = [] for start in self._adjacents.keys(): for end in self.adjacents(start): if start != end: edges.append((start, end)) return edges
Subclasses
Methods
def add_adjacent_vertex(self, start_label: str, end_label: str)
-
Add an undirected vertex to the adjacency list (same as add_edge()).
Args
start_label
- starting vertex label
end_label
- ending vertex label
def add_directed_adjacent_vertex(self, start_label: str, end_label: str)
-
Add a directed vertex to the adjacency list (same as add_directed_edge()).
Args
start_label
- starting vertex label
end_label
- ending vertex label
def add_directed_edge(self, start_label: str, end_label: str)
-
Add a directed vertex to the adjacency list (same as add_directed_adjacent_vertex()).
Args
start_label
- starting vertex label
end_label
- ending vertex label
def add_edge(self, start_label: str, end_label: str)
-
Add an undirected vertex to the adjacency list (same as add_adjacent_vertex()).
Args
start_label
- starting vertex label
end_label
- ending vertex label
def adjacents(self, vertex: str) ‑> list
-
Return a list of adjacents of a given vertex
Args
vertex
- starting vertex label
def bf_traverse(self, start_label: str)
-
Return a list of vertices through breadth first traversal starting at a given vertex
Args
start_label
- starting vertex label
Returns
a list of vertex labels
def bfs(self, start_label: str, end_label: str) ‑> str
-
breadth first search.
Args
start_label
- beginning vertex
end_label
- vertex to search for
Returns: vertex in the graph if found, None otherwise.
def df_traverse(self, start_label: str)
-
Return a list of vertices through depth first traversal starting at a given vertex
Args
start_label
- starting vertex label
Returns
a list of vertex labels
def dfs(self, start_label: str, end_label: str) ‑> str
-
Recursive depth first search.
Args
start
- beginning vertex
end
- vertex to search for
Returns: vertex in the graph if found, None otherwise.
def edges(self)
-
Return a list of edges in the graph. Each edge is represented by a tuple (start, end)
def is_edge(self, start_label: str, end_label: str) ‑> bool
-
Return boolean if an edge exists
Args
start_label
- starting vertex label
end_label
- starting vertex label
Returns
boolean of whether there is an edge from start to end
def vertices(self) ‑> list
-
" return a list of vertex labels of the graph
class AdjacencyListWeightedGraph
-
A weighted adjacency list vertex implementation in Python (allows either directed or undirected representation)
Expand source code
class AdjacencyListWeightedGraph(AdjacencyListGraph): """ A weighted adjacency list vertex implementation in Python (allows either directed or undirected representation) """ def __init__(self): #: hash table of vertices in graph self._adjacents = {} def add_adjacent_vertex(self, start_label: str, end_label: str, weight): """ Add an undirected vertex to the adjacency list (same as add_edge()). Args: start: starting vertex label end: ending vertex label weight: edge weight """ self.add_directed_adjacent_vertex(start_label, end_label, weight) if end_label not in self._adjacents: self._adjacents[end_label] = {} self._adjacents[end_label][start_label] = weight def add_edge(self, start_label: str, end_label: str, weight): """ Add an undirected vertex to the adjacency list (same as add_adjacent_vertex()). Args: start: starting vertex label end: ending vertex label weight: edge weight """ self.add_adjacent_vertex(start_label, end_label, weight) def add_directed_edge(self, start_label: str, end_label: str, weight): """ Add a directed vertex to the adjacency list (same as add_directed_adjacent_vertex()). Args: start: starting vertex label end: ending vertex label weight: edge weight """ self.add_directed_adjacent_vertex(start_label, end_label, weight) def add_directed_adjacent_vertex(self, start_label: str, end_label: str, weight): """ Add a directed vertex to the adjacency list (same as add_directed_edge()). Args: start: starting vertex label end: ending vertex label weight: edge weight """ if start_label not in self._adjacents: self._adjacents[start_label] = {} self._adjacents[start_label][end_label] = weight if end_label not in self._adjacents: self._adjacents[end_label] = {} def adjacents(self, vertex: str) -> list: """ Return a list of adjacents of a given vertex Args: vertex: starting vertex label """ return self._adjacents[vertex] def df_traverse(self): """ Perform depth first traversal. """ return self._df_traverse_rec(self, set()) def _df_traverse_rec(self, vertex, visited=None): """ helper depth first traversal recursive function """ visited.add(vertex) for v in vertex.adjacents: if v not in visited: v._df_traverse_rec(v, visited) def bf_traverse(self): """ Perform breadth first traversal. """ start = self visited = set() queue = Queue() queue.enqueue(start) while len(queue) > 0: current = queue.dequeue() if current not in visited: visited.add(current) for v in current.adjacents: queue.enqueue(v) def dfs(self, start_label: str, end_label: str) -> str: """ Recursive depth first search. Args: start_label: beginning vertex end_label: vertex to search for Returns: vertex in the graph none if not found. """ return self.dfs_rec(start_label, end_label, set()) def dfs_rec(self, current, end_label, visited=None): """ Helper depth-first search recursive function. Args: current: Current vertex end_label: Target vertex label visited: Set of visited vertices Returns: vertex in the graph if found, None otherwise. """ if visited is None: visited = set() if current == end_label: return current visited.add(current) for v in self.adjacents(current): if v not in visited: result = self.dfs_rec(v, end_label, visited) if result is not None: return result return None def bfs(self, start_label: str, end_label: str) -> str: """ Recursive breadth first search. Args: end: vertex to search for Returns: vertex in the graph None if not found. """ visited = set() queue = Queue() visited.add(start_label) queue.enqueue(start_label) while not queue.is_empty(): current = queue.dequeue() if current == end_label: return current for v in self[current]: if v not in visited: visited.add(v) queue.enqueue(v) return None def __getitem__(self, vertex: str): """ Args: vertex: vertex label Returns: Return a list of adjacent vertices """ return self._adjacents[vertex] def __len__(self): return len(self._adjacents) def edges(self): """ Return a list of edges in the graph. Each edge is represented by a tuple (start, end, weight) """ edges = [] for start in self._adjacents.keys(): for end in self.adjacents(start): weight = self[start][end] if start != end: edges.append((start, end, weight)) return edges def is_edge(self, start_label: str, end_label: str) -> bool: """ Return boolean if an edge exists Args: start_label: starting vertex label end_label: starting vertex label Returns: boolean of whether there is an edge from start to end """ return end_label in self[start_label]
Ancestors
Methods
def add_adjacent_vertex(self, start_label: str, end_label: str, weight)
-
Add an undirected vertex to the adjacency list (same as add_edge()).
Args
start
- starting vertex label
end
- ending vertex label
weight
- edge weight
def add_directed_adjacent_vertex(self, start_label: str, end_label: str, weight)
-
Add a directed vertex to the adjacency list (same as add_directed_edge()).
Args
start
- starting vertex label
end
- ending vertex label
weight
- edge weight
def add_directed_edge(self, start_label: str, end_label: str, weight)
-
Add a directed vertex to the adjacency list (same as add_directed_adjacent_vertex()).
Args
start
- starting vertex label
end
- ending vertex label
weight
- edge weight
def add_edge(self, start_label: str, end_label: str, weight)
-
Add an undirected vertex to the adjacency list (same as add_adjacent_vertex()).
Args
start
- starting vertex label
end
- ending vertex label
weight
- edge weight
def bf_traverse(self)
-
Perform breadth first traversal.
def bfs(self, start_label: str, end_label: str) ‑> str
-
Recursive breadth first search.
Args
end
- vertex to search for
Returns: vertex in the graph None if not found.
def df_traverse(self)
-
Perform depth first traversal.
def dfs(self, start_label: str, end_label: str) ‑> str
-
Recursive depth first search.
Args
start_label
- beginning vertex
end_label
- vertex to search for
Returns: vertex in the graph none if not found.
def dfs_rec(self, current, end_label, visited=None)
-
Helper depth-first search recursive function.
Args: current: Current vertex end_label: Target vertex label visited: Set of visited vertices
Returns: vertex in the graph if found, None otherwise.
def edges(self)
-
Return a list of edges in the graph. Each edge is represented by a tuple (start, end, weight)
Inherited members
class AdjacencyMatrixGraph (labels: list[str])
-
An unweighted adjacency matrix graph implementation (allows either directed or undirected representation) vertex labels are string types
Args
labels
- list of labels for each vertex
Expand source code
class AdjacencyMatrixGraph: """ An unweighted adjacency matrix graph implementation (allows either directed or undirected representation) vertex labels are string types """ def __init__(self, labels: list[str]): """ Args: labels: list of labels for each vertex """ self.labels = labels self.label_index = { label: index for index, label in enumerate(labels) } node_count = len(self.labels) self.array = [[None for i in range(node_count)] for j in range(node_count)] def add_edge(self, a_label: str, b_label: str): """ Add an undirected edge between one vertex to another (same as add_edge()) Args: a_label: starting vertex label b_label: ending vertex label """ self.add_adjacent_vertex(a_label, b_label) def add_adjacent_vertex(self, a_label: str, b_label: str): """ Add an undirected edge between one vertex to another (same as add_adjacent_vertex()). Args: a_label: starting vertex label b_label: ending vertex label """ a = self.label_index[a_label] b = self.label_index[b_label] self.array[a][b] = True self.array[a][a] = True self.array[b][a] = True self.array[b][b] = True def add_directed_edge(self, a_label: str, b_label: str): """ Add a directed edge between one vertex to another (same as add_directed_adjacent_vertex() and add_adjacent_directed_vertex()) Args: a_label: starting vertex label b_label: ending vertex label """ self.add_adjacent_directed_vertex(a_label, b_label) def add_directed_adjacent_vertex(self, a_label: str, b_label: str): """ Add a directed edge between one vertex to another (same as add_adjacent_directed_vertex() and add_directed_edge()) Args: a_label: starting vertex label b_label: ending vertex label """ self.add_adjacent_directed_vertex(a_label, b_label) def add_adjacent_directed_vertex(self, a_label: str, b_label: str): """ Add a directed edge between one vertex to another (same as add_directed_adjacent_vertex() and add_directed_edge()) Args: a_label: starting vertex label b_label: ending vertex label """ a = self.label_index[a_label] b = self.label_index[b_label] self.array[a][b] = True self.array[a][a] = True self.array[b][b] = True def df_traverse(self, start_label: str): """ Perform depth first traversal in an adjacency matrix Args: start_label: starting vertex label """ return self._df_rec_traverse(start_label, set(), []) def _df_rec_traverse(self, start_label: str, visited, dfs): """ Helper method for depth first recursive traversal """ start_index = self.label_index[start_label] visited.add(start_label) dfs.append(self.labels[start_index]) for adj_index, is_connected in enumerate(self.array[start_index]): adj_label = self.labels[adj_index] if is_connected and adj_label not in visited: self._df_rec_traverse(adj_label, visited, dfs) return dfs def bf_traverse(self, start_label: str): """ Perform breadth first traversal in an adjacency matrix Args: start_label: starting vertex label Returns: array with breadth first order traversal """ bfs = [] q = Queue() visited = set() start_index = self.label_index[start_label] q.enqueue(start_index) bfs.append(self.labels[start_index]) while not q.is_empty(): current = q.dequeue() for i in range(len(self.array)): if start_index != i and self.array[current][i] and (i not in visited): visited.add(i) q.enqueue(i) bfs.append(self.labels[i]) return bfs def vertices(self) -> list: """" return a list of vertex labels of the graph """ return self.labels def edges(self) -> list: """ Return a list of edges in the graph. Each edge is represented by a tuple (start, end) """ edges = [] vertex_count = len(self.labels) for i in range(vertex_count): for j in range(vertex_count): if self.array[i][j] and i != j: edges.append((self.labels[i], self.labels[j])) return edges def is_edge(self, start: str, end: str) -> bool: """ Return boolean if an edge exists Args: start_label: starting vertex label end_label: starting vertex label Returns: boolean of whether there is an edge from start to end """ start_index = self.label_index[start] end_index = self.label_index[end] return self.array[start_index][end_index] def __getitem__(self, vertex: str) -> list: """ Args: vertex: vertex label Returns: a list of adjacent vertex labels """ index = self.label_index[vertex] return [self.labels[i] for i in range(len(self.array[index])) if self.array[index][i]] def print_graph(self): """ Print the contents of the graph """ print(" |", end="") for label in self.labels: print(f"{label:^3}", end=" ") print() print("----" * (len(self.array) + 1)) for r, row in enumerate(self.array): label = self.labels[r] print(f"{label:^3}|", end="") for col in row: b = " T " if col else " " print(b, end=" ") print()
Subclasses
Methods
def add_adjacent_directed_vertex(self, a_label: str, b_label: str)
-
Add a directed edge between one vertex to another (same as add_directed_adjacent_vertex() and add_directed_edge())
Args
a_label
- starting vertex label
b_label
- ending vertex label
def add_adjacent_vertex(self, a_label: str, b_label: str)
-
Add an undirected edge between one vertex to another (same as add_adjacent_vertex()).
Args
a_label
- starting vertex label
b_label
- ending vertex label
def add_directed_adjacent_vertex(self, a_label: str, b_label: str)
-
Add a directed edge between one vertex to another (same as add_adjacent_directed_vertex() and add_directed_edge())
Args
a_label
- starting vertex label
b_label
- ending vertex label
def add_directed_edge(self, a_label: str, b_label: str)
-
Add a directed edge between one vertex to another (same as add_directed_adjacent_vertex() and add_adjacent_directed_vertex())
Args
a_label
- starting vertex label
b_label
- ending vertex label
def add_edge(self, a_label: str, b_label: str)
-
Add an undirected edge between one vertex to another (same as add_edge())
Args
a_label
- starting vertex label
b_label
- ending vertex label
def bf_traverse(self, start_label: str)
-
Perform breadth first traversal in an adjacency matrix
Args
start_label
- starting vertex label
Returns
array with breadth first order traversal
def df_traverse(self, start_label: str)
-
Perform depth first traversal in an adjacency matrix
Args
start_label
- starting vertex label
def edges(self) ‑> list
-
Return a list of edges in the graph. Each edge is represented by a tuple (start, end)
def is_edge(self, start: str, end: str) ‑> bool
-
Return boolean if an edge exists
Args
start_label
- starting vertex label
end_label
- starting vertex label
Returns
boolean of whether there is an edge from start to end
def print_graph(self)
-
Print the contents of the graph
def vertices(self) ‑> list
-
" return a list of vertex labels of the graph
class AdjacencyMatrixWeightedGraph (labels)
-
A weighted adjacency matrix graph implementation (allows either directed or undirected representation) vertex labels are string types
Args
labels
- list of labels for each vertex (string types)
Expand source code
class AdjacencyMatrixWeightedGraph(AdjacencyMatrixGraph): """ A weighted adjacency matrix graph implementation (allows either directed or undirected representation) vertex labels are string types """ def __init__(self, labels): """ Args: labels: list of labels for each vertex (string types) """ super().__init__(labels) def add_edge(self, a_label: str, b_label: str, weight): """ Add an undirected edge between one vertex to another (same as add_edge()) Args: a_label: starting vertex label b_label: ending vertex label weight: weight of the vertex """ self.add_adjacent_vertex(a_label, b_label, weight) def add_adjacent_vertex(self, a_label: str, b_label: str, weight): """ Add an undirected edge between one vertex to another (same as add_edge()) Args: a_label: starting vertex label b_label: ending vertex label weight: weight of the vertex """ a = self.label_index[a_label] b = self.label_index[b_label] self.array[a][b] = weight self.array[a][a] = 0 self.array[b][a] = weight self.array[b][b] = 0 def add_directed_edge(self, a_label: str, b_label: str, weight): """ Add a weighted directed edge between one vertex to another (same as add_adjacent_directed_vertex(), add_directed_adjacent_vertex()) Args: a_label: starting vertex label b_label: ending vertex label weight: weight of the vertex """ self.add_adjacent_directed_vertex(a_label, b_label, weight) def add_directed_adjacent_vertex(self, a_label: str, b_label: str, weight): """ Add a weighted directed edge between one vertex to another (same as add_directed_edge(), add_adjacent_directed_vertex()) Args: a_label: starting vertex label b_label: ending vertex label weight: weight of the vertex """ self.add_adjacent_directed_vertex(a_label, b_label, weight) def add_adjacent_directed_vertex(self, a_label: str, b_label: str, weight): """ Add a weighted directed edge between one vertex to another (same as add_directed_edge(), add_directed_adjacent_vertex()) Args: a_label: starting vertex label b_label: ending vertex label weight: weight of the vertex """ a = self.label_index[a_label] b = self.label_index[b_label] self.array[a][b] = weight self.array[a][a] = 0 self.array[b][b] = 0 def print_graph(self): """ Print the contents of the graph. """ print(" |", end="") for label in self.labels: print(f"{label:>3}", end=" ") print() print("----" * (len(self.array) + 1)) for r, row in enumerate(self.array): label = self.labels[r] print(f"{label:^3}|", end="") for col in row: w = f"{col:3}" if col is not None else " " print(w, end=" ") print() def edges(self) -> list: """ Return a list of edges in the graph. Each edge is represented by a tuple (start, end, weight) """ edges = [] vertex_count = len(self.labels) for i in range(vertex_count): for j in range(vertex_count): weight = self.array[i][j] if weight and i != j: edges.append((self.labels[i], self.labels[j], weight)) return edges def is_edge(self, start: str, end: str) -> bool: """ Return boolean if an edge exists Args: start_label: starting vertex label end_label: starting vertex label Returns: boolean of whether there is an edge from start to end """ return super().is_edge(start, end) is not None def weightx(self, start: str, end: str) -> bool: """ Return weight of an edge Args: start_label: starting vertex label end_label: starting vertex label Returns: weight value of an edge from start to end """ return super().is_edge(start, end) def __getitem__(self, vertex: str) -> dict: """ Args: vertex: vertex label Returns: a dictionary of adjacent vertex labels and weights """ index = self.label_index[vertex] return {self.labels[i] : self.array[index][i] for i in range(len(self.array[index])) if self.array[index][i]}
Ancestors
Methods
def add_adjacent_directed_vertex(self, a_label: str, b_label: str, weight)
-
Add a weighted directed edge between one vertex to another (same as add_directed_edge(), add_directed_adjacent_vertex())
Args
a_label
- starting vertex label
b_label
- ending vertex label
weight
- weight of the vertex
def add_adjacent_vertex(self, a_label: str, b_label: str, weight)
-
Add an undirected edge between one vertex to another (same as add_edge())
Args
a_label
- starting vertex label
b_label
- ending vertex label
weight
- weight of the vertex
def add_directed_adjacent_vertex(self, a_label: str, b_label: str, weight)
-
Add a weighted directed edge between one vertex to another (same as add_directed_edge(), add_adjacent_directed_vertex())
Args
a_label
- starting vertex label
b_label
- ending vertex label
weight
- weight of the vertex
def add_directed_edge(self, a_label: str, b_label: str, weight)
-
Add a weighted directed edge between one vertex to another (same as add_adjacent_directed_vertex(), add_directed_adjacent_vertex())
Args
a_label
- starting vertex label
b_label
- ending vertex label
weight
- weight of the vertex
def add_edge(self, a_label: str, b_label: str, weight)
-
Add an undirected edge between one vertex to another (same as add_edge())
Args
a_label
- starting vertex label
b_label
- ending vertex label
weight
- weight of the vertex
def edges(self) ‑> list
-
Return a list of edges in the graph. Each edge is represented by a tuple (start, end, weight)
def print_graph(self)
-
Print the contents of the graph.
def weightx(self, start: str, end: str) ‑> bool
-
Return weight of an edge
Args
start_label
- starting vertex label
end_label
- starting vertex label
Returns
weight value of an edge from start to end
Inherited members