代码的主要实现内容有:
- 图的表示
- 图的遍历
- 广度优先遍历
- 深度优先遍历
- 最短路径算法
- 单点最短路径算法:Dijkstra
- 多点最短路径算法:Floyd
- 拓扑排序
import copy
class Edge(object):
def __init__(self, node, weight, name=""):
self.node = node
self.weight = weight
self.name = name
self.node.in_degree += 1
class Node(object):
def __init__(self, name=""):
self.edges = []
self.name = name
self.in_degree = 0
def add_edge(self, edge):
self.edges.append(edge)
class Graph(object):
def __init__(self, nodes: list = None, name=""):
self.name = name
self.name_of_nodes = set()
self.__index = 0
if nodes is None:
self.nodes = []
else:
for node in nodes:
self.check_node_name(node)
self.nodes = nodes
# 检查节点名称是否唯一
def check_node_name(self, node):
if node.name in self.name_of_nodes:
raise ValueError("Node name must be unique.")
if node.name == "":
node.name = str(self.__index)
self.__index += 1
self.name_of_nodes.add(node.name)
@staticmethod
def from_adjacency_matrix(matrix: list, name=""):
nodes = [Node(name=str(i)) for i in range(len(matrix))]
for i in range(len(matrix)):
for j in range(len(matrix[i])):
if matrix[i][j] != 0:
nodes[i].add_edge(Edge(nodes[j], matrix[i][j]))
return Graph(nodes, name)
def add_node(self, node):
self.check_node_name(node)
self.nodes.append(node)
def add_edge(self, node1, node2, weight=1, is_directed=False):
node1.add_edge(Edge(node2, weight)) # node1 -> node2
if not is_directed:
node2.add_edge(Edge(node1, weight)) # node2 -> node1
# 广度优先搜索
@staticmethod
def bfs(graph, start_node: Node):
nodes = []
queue = [start_node]
visited = set()
while queue:
node = queue.pop(0)
if node not in visited:
nodes.append(node)
visited.add(node)
for edge in node.edges:
queue.append(edge.node)
return nodes
# 深度优先搜索
@staticmethod
def dfs(graph, start_node: Node):
nodes = []
stack = [start_node]
visited = set()
while stack:
node = stack.pop()
if node not in visited:
nodes.append(node)
visited.add(node)
for edge in node.edges:
stack.append(edge.node)
return nodes
# 最短路径,Dijkstra算法
@staticmethod
def dijkstra(graph, start_node: Node, end_node: Node):
s = [start_node] # 已经找到最短路径的节点集合
v = set(graph.nodes) # 未找到最短路径的节点集合
v.remove(start_node)
d = {node: float("inf") for node in graph.nodes} # 起点到各个节点的最短路径长度
d[start_node] = 0
p = {node: None for node in graph.nodes} # 起点到各个节点的最短路径上的前一个节点
while v:
curr = s[-1]
for edge in curr.edges:
if edge.node in v and d[curr] + edge.weight < d[edge.node]:
d[edge.node] = d[curr] + edge.weight # 更新最短路径长度
p[edge.node] = curr # 更新前一个节点
# 从v中找到最短路径的节点
min_node = None
for node in v:
if min_node is None:
min_node = node
elif d[node] < d[min_node]:
min_node = node
s.append(min_node) # 加入s
v.remove(min_node) # 从v中移除
path = [end_node] # 最短路径, 从终点开始, 往前找
while p[end_node] is not None:
path.append(p[end_node])
end_node = p[end_node]
path.reverse()
return path, d[path[-1]]
# Floyd算法,求任意两点之间的最短路径
@staticmethod
def floyd(graph):
# 初始化,d[i][j]表示从i到j的最短路径长度,p[i][j]表示从i到j的最短路径上的前一个节点
d = {node: {node2: 0 if node == node2 else float("inf") for node2 in graph.nodes} for node in graph.nodes}
p = {node: {node2: None for node2 in graph.nodes} for node in graph.nodes}
for node in graph.nodes:
for edge in node.edges:
d[node][edge.node] = edge.weight
p[node][edge.node] = node
# 计算最短路径长度和前一个节点,满足d[i][j] = min(d[i][j], d[i][k] + d[k][j])
for k in graph.nodes:
for i in graph.nodes:
for j in graph.nodes:
if d[i][k] + d[k][j] < d[i][j]:
d[i][j] = d[i][k] + d[k][j]
p[i][j] = p[k][j]
return d, p
# 计算最短路径中的节点顺序
@staticmethod
def get_path(p, start_node, end_node):
path = [end_node]
while p[start_node][end_node] is not None:
path.append(p[start_node][end_node])
end_node = p[start_node][end_node]
path.reverse()
return path
# 拓扑排序
@staticmethod
def topological_sort(graph):
g_nodes = [copy.deepcopy(node) for node in graph.nodes]
nodes = []
queue = [node for node in g_nodes if node.in_degree == 0]
while queue:
node = queue.pop(0)
nodes.append(node)
for edge in node.edges:
edge.node.in_degree -= 1
if edge.node.in_degree == 0:
queue.append(edge.node)
return nodes
if __name__ == "__main__":
# 测试最短路径,不同权值的边
matrix = [
[0,9,0,3,0],
[0,0,7,0,1],
[0,0,0,0,6],
[0,0,0,0,8],
[0,0,0,0,0]
]
graph = Graph.from_adjacency_matrix(matrix)
start_node = graph.nodes[0]
end_node = graph.nodes[4]
path, d = Graph.dijkstra(graph, start_node, end_node)
print("dijkstra:\n" + " -> ".join([node.name for node in path]))
print(d)
x, y = Graph.floyd(graph)
print("floyd:\n" + " -> ".join([node.name for node in Graph.get_path(y, start_node, end_node)]))
print(x[start_node][end_node])
# 测试bfs, dfs
n = Graph.bfs(graph, start_node)
print("bfs:\n" + " -> ".join([node.name for node in n]))
n = Graph.dfs(graph, start_node)
print("dfs:\n" + " -> ".join([node.name for node in n]))
# 测试拓扑排序
z = Graph.topological_sort(graph)
print("topological sort:\n" + " -> ".join([node.name for node in z]))