156 lines
5.2 KiB
Python
156 lines
5.2 KiB
Python
"""
|
||
无向图:边表示两顶点之间的双向链接关系,如 A---B,即仅通过一条边,A 可达到 B,B 也可达到 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}')
|