最短路径问题:
Dijkstra算法
import heapq
def dijkstra(adj_list, start):
# 初始化距离字典
distances = {node: float('inf') for node in adj_list}
distances[start] = 0
# 优先队列,存储 (距离, 节点) 元组
pq = [(0, start)]
while pq:
# 弹出距离最小的节点
dist, node = heapq.heappop(pq)
# 如果当前距离大于已知最短距离,则跳过
if dist > distances[node]:
continue
# 遍历邻居节点
for neighbor, weight in adj_list[node]:
# 计算新的距离
new_dist = distances[node] + weight
# 如果新的距离更短,则更新距离字典和优先队列
if new_dist < distances[neighbor]:
distances[neighbor] = new_dist
heapq.heappush(pq, (new_dist, neighbor))
return distances
# 示例使用
adj_list = {
'A': [('B', 2), ('C', 4)],
'B': [('A', 2), ('C', 1), ('D', 5)],
'C': [('A', 4), ('B', 1), ('D', 3)],
'D': [('B', 5), ('C', 3)]
}
start_node = 'A'
distances = dijkstra(adj_list, start_node)
print("Shortest distances from node", start_node)
for node, distance in distances.items():
print("To node", node, "- Distance:", distance)
Floyd-Warshall算法
import sys
def floyd_warshall(adj_matrix):
n = len(adj_matrix)
# 创建一个新的矩阵来存储最短路径长度
dist = [[0] * n for _ in range(n)]
# 初始化最短路径矩阵为邻接矩阵
for i in range(n):
for j in range(n):
dist[i][j] = adj_matrix[i][j]
# 通过中间节点的遍历更新最短路径矩阵
for k in range(n):
for i in range(n):
for j in range(n):
dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j])
return dist
# 示例使用
# 用sys.maxsize表示无穷大
inf = sys.maxsize
# 示例邻接矩阵
adj_matrix = [
[0, 5, inf, 10],
[inf, 0, 3, inf],
[inf, inf, 0, 1],
[inf, inf, inf, 0]
]
# 调用floyd_warshall函数计算最短路径矩阵
shortest_paths = floyd_warshall(adj_matrix)
# 打印最短路径矩阵
print("Shortest Paths:")
for row in shortest_paths:
print(row)
最小生成树问题:
Prim算法
1.未优化
import heapq
def prim(adj_list):
# 选取任意一个节点作为起始节点
start_node = list(adj_list.keys())[0]
# 初始化访问状态和最小生成树
visited = {node: False for node in adj_list}
visited[start_node] = True
min_spanning_tree = []
# 优先队列,存储 (边权重, 起始节点, 目标节点) 元组
pq = [(weight, start_node, neighbor) for neighbor, weight in adj_list[start_node]]
heapq.heapify(pq)
while pq:
# 弹出权重最小的边
weight, src, dest = heapq.heappop(pq)
# 如果目标节点已访问,则跳过
if visited[dest]:
continue
# 将边加入最小生成树
min_spanning_tree.append((src, dest, weight))
# 将目标节点标记为已访问
visited[dest] = True
# 将目标节点的邻居边加入优先队列
for neighbor, weight in adj_list[dest]:
if not visited[neighbor]:
heapq.heappush(pq, (weight, dest, neighbor))
return min_spanning_tree
# 示例使用
adj_list = {
'A': [('B', 2), ('C', 4)],
'B': [('A', 2), ('C', 1), ('D', 5)],
'C': [('A', 4), ('B', 1), ('D', 3)],
'D': [('B', 5), ('C', 3)]
}
min_spanning_tree = prim(adj_list)
print("Minimum Spanning Tree:")
for src, dest, weight in min_spanning_tree:
print(src, "--", dest, ": Weight =", weight)
2.减少目标节点的访问次数,只需进行两次比较
import heapq
def prim(adj_list):
# 选取任意一个节点作为起始节点
start_node = list(adj_list.keys())[0]
# 初始化访问状态和最小生成树
visited = {node: False for node in adj_list}
visited[start_node] = True
min_spanning_tree = []
# 优先队列,存储 (边权重, 起始节点, 目标节点) 元组
pq = [(weight, start_node, neighbor) for neighbor, weight in adj_list[start_node]]
heapq.heapify(pq)
while pq:
# 弹出权重最小的边
weight, src, dest = heapq.heappop(pq)
# 如果目标节点已访问,则跳过
if visited[dest]:
continue
# 将边加入最小生成树
min_spanning_tree.append((src, dest, weight))
# 将目标节点标记为已访问
visited[dest] = True
# 将目标节点的邻居边加入优先队列(只加入未访问的邻居节点的边)
for neighbor, weight in adj_list[dest]:
if not visited[neighbor]:
# 通过比较目标节点的权重,更新已存在于优先队列中的边
for i, (old_weight, old_src, old_dest) in enumerate(pq):
if old_dest == neighbor and weight < old_weight:
pq[i] = (weight, dest, neighbor)
heapq.heapify(pq)
break
else:
heapq.heappush(pq, (weight, dest, neighbor))
return min_spanning_tree
# 示例使用
adj_list = {
'A': [('B', 2), ('C', 4)],
'B': [('A', 2), ('C', 1), ('D', 5)],
'C': [('A', 4), ('B', 1), ('D', 3)],
'D': [('B', 5), ('C', 3)]
}
min_spanning_tree = prim(adj_list)
print("Minimum Spanning Tree:")
for src, dest, weight in min_spanning_tree:
print(src, "--", dest, ": Weight =", weight)
kruskal算法
1.并查集方法判断环
#并查集方法判断是否有环
class DisjointSet:
def __init__(self, n):
self.parent = list(range(n))
self.rank = [0] * n
def find(self, x):
# 查找节点的根节点
if self.parent[x] != x:
# 路径压缩:将节点的父节点直接设为根节点,减少后续查找时间
self.parent[x] = self.find(self.parent[x])
return self.parent[x]
def union(self, x, y):
x_root = self.find(x)
y_root = self.find(y)
if x_root == y_root:
return
if self.rank[x_root] < self.rank[y_root]:
self.parent[x_root] = y_root
elif self.rank[x_root] > self.rank[y_root]:
self.parent[y_root] = x_root
else:
self.parent[y_root] = x_root
self.rank[x_root] += 1
def kruskal(vertices, edges):
edges.sort(key=lambda x: x[2]) # 按边权重升序排序
num_vertices = len(vertices)
min_spanning_tree = []
ds = DisjointSet(num_vertices)
for edge in edges:
src, dest, weight = edge
src_root = ds.find(src)
dest_root = ds.find(dest)
if src_root != dest_root:
# 如果边的起始节点和目标节点不在同一个连通分量中,则将它们合并,并将该边加入最小生成树列表
ds.union(src_root, dest_root)
min_spanning_tree.append(edge)
return min_spanning_tree
# 示例使用
vertices = ['A', 'B', 'C', 'D', 'E']
edges = [
('A', 'B', 2),
('A', 'C', 3),
('B', 'C', 1),
('B', 'D', 4),
('C', 'D', 2),
('C', 'E', 5),
('D', 'E', 3)
]
min_spanning_tree = kruskal(vertices, edges)
print("Minimum Spanning Tree:")
for src, dest, weight in min_spanning_tree:
print(src, "--", dest, ": Weight =", weight)
2.是否添加新节点判断有环
def kruskal(vertices, edges):
num_vertices = len(vertices)
added_to_tree = [False] * num_vertices # 记录顶点是否已添加到树中
min_spanning_tree = []
edges.sort(key=lambda x: x[2]) # 按边权重升序排序
for edge in edges:
src, dest, weight = edge
src_index = vertices.index(src)
dest_index = vertices.index(dest)
if not added_to_tree[src_index] or not added_to_tree[dest_index]:
min_spanning_tree.append(edge)
added_to_tree[src_index] = True
added_to_tree[dest_index] = True
return min_spanning_tree
# 示例使用
vertices = ['A', 'B', 'C', 'D', 'E']
edges = [
('A', 'B', 2),
('A', 'C', 3),
('B', 'C', 1),
('B', 'D', 4),
('C', 'D', 2),
('C', 'E', 5),
('D', 'E', 3)
]
min_spanning_tree = kruskal(vertices, edges)
print("Minimum Spanning Tree:")
for src, dest, weight in min_spanning_tree:
print(src, "--", dest, ": Weight =", weight)