pygorithm/graph/graph.py

156 lines
5.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

"""
无向图:边表示两顶点之间的双向链接关系,如 A---B即仅通过一条边A 可达到 BB 也可达到 A
有向图:边具有方向性,如 A --> B 或 B <-- A即通过一条边只能由 A 向 B 或 B 向 A
连通图:从某个顶点出发,可以到达其余任意顶点。
非连通图:从某个顶点出发,至少有一个顶点无法到达。
邻接adjacency当两顶点之间存在边相连时称这两顶点“邻接“
路径path从顶点 A 到顶点 B 经过的边构成的序列被称为从 A 到 B 的“路径
degree一个顶点拥有的边数
有权图:每条边都具有一个值(权重),表示从一个顶点到另一个顶点之间的代价/开销等....
"""
"""
邻接矩阵:设图的顶点数量为 n邻接矩阵使用一个 n*n大小的矩阵来表示图表示一个顶点
矩阵元素代表边用1或0表示两个顶点之间是否存在边
1 3 5 6 顶点
1 0 1 1 0
3 1 0 1 0
5 1 1 0 1
6 0 1 0 0
注意这里表示的是无向图,关于矩阵关于主对角线对称
将 1 和 0 替换为其他的值,则可表示有权图
"""
# 实现一个邻接矩阵
class GraphAdjMat:
def __init__(self, vertices: list[int], edges: list[list[int]]):
self.vertices: list[int] = []
self.adj_mat: list[list[int]] = []
# 添加顶点
for val in vertices:
self.add_vertex(val)
for e in edges:
self.add_edge(e[0], e[1])
def size(self) -> int:
return len(self.vertices)
def add_vertex(self, val: int):
n = self.size()
self.vertices.append(val)
# 在邻接矩阵中添加一行
new_row = [0] * n
self.adj_mat.append(new_row)
# 向邻接矩阵中添加一列
for row in self.adj_mat:
row.append(0)
def add_edge(self, i:int, j:int):
# 参数 i, j 对应 vertices 元素索引
if i < 0 or j < 0 or i >= self.size() or j >= self.size():
raise IndexError()
# 无向图中,邻接矩阵关于主对角线对称
self.adj_mat[i][j] = 1
self.adj_mat[j][i] = 1
def remove_vertex(self, index: int):
if index >= self.size():
raise IndexError()
self.vertices.pop(index)
# 删除对应索引的行
self.adj_mat.pop(index)
# 删除对应索引的列
for row in self.adj_mat:
row.pop(index)
def remove_edge(self, i: int, j: int):
if i < 0 or j < 0 or i >= self.size() or j >= self.size() or i == j:
raise IndexError()
self.adj_mat[i][j] = 0
self.adj_mat[j][i] = 0
"""
设无向图的顶点总数为 n、边总数为 m
添加边:在顶点对应链表的末尾添加边即可,因为是无向图,所以需要同时添加两个方向的边。
删除边:在顶点对应链表中查找并删除指定边,在无向图中,需要同时删除两个方向的边
添加顶点:在邻接表中添加一个链表,并将新增顶点作为链表头节点
删除顶点:需遍历整个邻接表,删除包含指定顶点的所有边
初始化:在邻接表中创建 n 个顶点和 2m 条边
"""
class Vertex:
"""顶点类"""
def __init__(self, val: int):
self.val = val
def vals_to_vets(vals: list[int]) -> list["Vertex"]:
"""输入值列表 vals ,返回顶点列表 vets"""
return [Vertex(val) for val in vals]
def vets_to_vals(vets: list["Vertex"]) -> list[int]:
"""输入顶点列表 vets ,返回值列表 vals"""
return [vet.val for vet in vets]
class GraphAdjList:
def __init__(self, edges: list[list[Vertex]]):
self.adj_list = dict[Vertex, list[Vertex]]()
for edge in edges:
self.add_vertex(edge[0])
self.add_vertex(edge[1])
self.add_edge(edge[0], edge[1])
def size(self):
return len(self.adj_list)
def add_edge(self, vet1: Vertex, vet2: Vertex):
if vet1 not in self.adj_list or vet2 not in self.adj_list or vet1 == vet2:
raise ValueError()
# 无向表,两边都需添加
self.adj_list[vet1].append(vet2)
self.adj_list[vet2].append(vet1)
def remove_edge(self, vet1: Vertex, vet2: Vertex):
if vet1 not in self.adj_list or vet2 not in self.adj_list:
raise ValueError()
self.adj_list[vet1].remove(vet2)
self.adj_list[vet2].remove(vet1)
def add_vertex(self, vet: Vertex):
"""添加顶点"""
if vet in self.adj_list:
return
# 在邻接表中添加一个新链表
self.adj_list[vet] = []
def remove_vertex(self, vet: Vertex):
"""删除顶点"""
if vet not in self.adj_list:
raise ValueError()
# 在邻接表中删除顶点 vet 对应的链表
self.adj_list.pop(vet)
# 遍历其他顶点的链表,删除所有包含 vet 的边
for vertex in self.adj_list:
if vet in self.adj_list[vertex]:
self.adj_list[vertex].remove(vet)
def print(self):
print("邻接表 =")
for vertex in self.adj_list:
tmp = [v.val for v in self.adj_list[vertex]]
print(f'{vertex.val}: {tmp}')