In this note are code for basic operations for Graph as adjacency list and Graph as adjacency matrix.
Base code
@dataclass(frozen=True, repr=False)
class Vertex[T]:
data: T
def __repr__(self) -> str:
return str(self.data)
type _V = Vertex[Any]
class BaseGraph(Protocol):
def add_vertex(self, vertex: _V) -> None: ...
def add_edge(
self, src: _V, dest: _V, weight: float = 1, directed: bool = False
) -> None: ...
def remove_edge(self, _V: _V, dest: _V, directed: bool = False) -> None: ...
def has_edge(self, src: _V, dest: _V) -> bool: ...
def display(self) -> None: ...
def dfs(self, start: _V) -> list[_V]:
to_visit: list[_V] = [start]
visited: set[_V] = {start}
path: list[_V] = []
while len(to_visit) != 0:
curr = to_visit.pop()
path.append(curr)
for neigh in self._get_neighs(curr):
if neigh not in visited:
to_visit.append(neigh)
visited.add(neigh)
return path
def bfs(self, start: _V) -> list[_V]:
to_visit: Deque[_V] = deque([start])
visited: set[_V] = set()
path: list[_V] = []
while len(to_visit) != 0:
curr = to_visit.popleft()
if curr not in visited:
visited.add(curr)
path.append(curr)
for neigh in self._get_neighs(curr):
to_visit.append(neigh)
return path
@abstractmethod
def _get_neighs(self, vertex: _V) -> list[_V]: ...Adjacency matrix
class AdjacencyMatrixGraph(BaseGraph):
def __init__(self, verteces: list[_V] | None = None) -> None:
self._verteces = []
self._adjacency_matrix: list[list[float]] = []
if verteces is not None:
for v in verteces:
self.add_vertex(v)
def add_vertex(self, vertex: _V) -> None:
if vertex in self._verteces:
raise ValueError(f"Vertex with data {vertex.data} already exists.")
self._verteces.append(vertex)
for row in self._adjacency_matrix:
row.append(0)
self._adjacency_matrix.append([0] * len(self._verteces))
def add_edge(
self, src: _V, dest: _V, weight: float = 1, directed: bool = False
) -> None:
src_i = self._verteces.index(src)
dest_i = self._verteces.index(dest)
self._adjacency_matrix[src_i][dest_i] = weight
if not directed:
self._adjacency_matrix[dest_i][src_i] = weight
def remove_edge(self, src: _V, dest: _V, directed: bool = False) -> None:
self.add_edge(src, dest, 0, directed)
def has_edge(self, src: _V, dest: _V) -> bool:
src_i = self._verteces.index(src)
dest_i = self._verteces.index(dest)
return self._adjacency_matrix[src_i][dest_i] != 0
def display(self) -> None:
print(" ", *self._verteces)
for vertex, row in zip(self._verteces, self._adjacency_matrix):
print(vertex, " ".join(map(str, row)))
def _get_neighs(self, vertex: _V) -> list[_V]:
vertex_i = self._verteces.index(vertex)
ret: list[_V] = []
for i, edge in enumerate(self._adjacency_matrix[vertex_i]):
if edge == 0:
continue
ret.append(self._verteces[i])
return retAdjacency list
class AdjacencyListGraph(BaseGraph):
def __init__(self, verteces: list[_V] | None = None) -> None:
self._adjacency_list: list[list[_V]] = []
if verteces is not None:
for v in verteces:
self.add_vertex(v)
def add_vertex(self, vertex: _V) -> None:
self._adjacency_list.append([vertex])
def add_edge(
self, src: _V, dest: _V, weight: float = 1, directed: bool = False
) -> None:
src_i = self._idx_from_vertex(src)
dest_i = self._idx_from_vertex(dest)
node = self._adjacency_list[dest_i][0]
self._adjacency_list[src_i].append(node)
if not directed:
node = self._adjacency_list[src_i][0]
self._adjacency_list[dest_i].append(node)
def remove_edge(self, src: _V, dest: _V, directed: bool = False) -> None:
src_i = self._idx_from_vertex(src)
dest_i = self._idx_from_vertex(dest)
node = self._adjacency_list[dest_i][0]
self._adjacency_list[src_i].remove(node)
def has_edge(self, src: _V, dest: Vertex) -> bool:
src_i = self._idx_from_vertex(src)
dest_i = self._idx_from_vertex(dest)
node = self._adjacency_list[dest_i][0]
return node in self._adjacency_list[src_i]
def display(self) -> None:
for list_ in self._adjacency_list:
print(list_)
def _get_neighs(self, vertex: _V) -> list[_V]:
return self._adjacency_list[self._idx_from_vertex(vertex)][1:]
def _idx_from_vertex(self, vertex: _V) -> int:
for i, v in enumerate(self._adjacency_list):
if v[0] == vertex:
return i
raise ValueError(f"Vertex with data {vertex.data} is not in the graph.")
Code usage example
a = Vertex("A")
b = Vertex("B")
c = Vertex("C")
d = Vertex("D")
e = Vertex("E")
verteces = [a, b, c, d, e]
m_graph = AdjacencyMatrixGraph(verteces)
a_graph = AdjacencyListGraph(verteces)
graph_impls: list[BaseGraph] = [m_graph, a_graph]
for graph in graph_impls graph.add_edge(a, b, directed=True)
graph.add_edge(b, c, directed=True)
graph.add_edge(b, e, directed=True)
graph.add_edge(c, d, directed=True)
graph.add_edge(c, e, directed=True)
graph.add_edge(e, a, directed=True)
graph.add_edge(e, c, directed=True)
print(graph.bfs(a))
print(graph.dfs(a))
print(graph.has_edge(e, c))
graph.remove_edge(e, c)
print(graph.has_edge(e, c))
graph.display()
print("\\n", "=" * 20, "\\n")Output
