📅  最后修改于: 2023-12-03 15:12:26.232000             🧑  作者: Mango
最小生成树算法是一类在带权连通图中寻找一棵权值最小的生成树的算法。其中,通过连接成本至少为0的任何顶点对来最小化连接图的成本。
Prim算法从单个节点开始,在每一步中,它将一个新节点加入到树中,这个新节点需要连接到已经树中的一个节点,被加入的节点是可以从树中已有节点到达的“最近”的节点。
from queue import PriorityQueue
def prim(graph, start):
"""
Prim算法实现最小生成树
:param graph: 图
{v1: {u1: w1, u2: w2, ...}, v2: {...}, ...}
:param start: 起点
:return: 生成树
{(v1, u1, w1), (v2, u2, w2), ...}
"""
# 初始化生成树集合
mst = set()
# 初始化已访问的节点集合
visited = set(start)
# 初始化候选边的队列
candidates = PriorityQueue()
# 将起点的所有出边加入候选边队列
for v, w in graph[start].items():
candidates.put((w, start, v))
# 循环直到生成树集合中包含所有节点或所有候选边遍历完
while candidates and len(visited) < len(graph):
# 取出队列中距离起点最近的边
w, u, v = candidates.get()
# 如果v已被访问过,说明这条边形成环,抛弃它
if v in visited:
continue
# 将这条边添加进生成树
mst.add((u, v, w))
# 将v加入到已访问的节点集合中
visited.add(v)
# 将v的所有出边加入候选边队列中
for next_v, next_w in graph[v].items():
if next_v not in visited:
candidates.put((next_w, v, next_v))
return mst
graph = {
'A': {'B': 2, 'C': 3},
'B': {'A': 2, 'C': 4, 'D': 3},
'C': {'A': 3, 'B': 4, 'D': 5},
'D': {'B': 3, 'C': 5},
}
mst = prim(graph, 'A')
print(mst)
# Output: {('A', 'B', 2), ('B', 'D', 3), ('A', 'C', 3)}
Kruskal算法是一种基于贪心算法的最小生成树算法,通过不断地选择边来将图中的点连接起来,但是在选择的过程中会过滤掉那些会形成环的边。
def kruskal(graph):
"""
Kruskal算法实现最小生成树
:param graph: 图
[(v1, u1, w1), (v2, u2, w2), ...]
:return: 生成树
[(v1, u1, w1), (v2, u2, w2), ...]
"""
# 初始化生成树集合
mst = []
# 初始化并查集
parent = {}
def find(x):
if x != parent[x]:
parent[x] = find(parent[x])
return parent[x]
def union(x, y):
parent[find(x)] = find(y)
for u, v, w in graph:
# 初始化并查集
parent.setdefault(u, u)
parent.setdefault(v, v)
# 如果u,v的根节点不同,说明这条边不会形成环,可以加入生成树
if find(u) != find(v):
mst.append((u, v, w))
union(u, v)
return mst
graph = [
('A', 'B', 2),
('A', 'C', 3),
('B', 'C', 4),
('B', 'D', 3),
('C', 'D', 5),
]
mst = kruskal(graph)
print(mst)
# Output: [('A', 'B', 2), ('A', 'C', 3), ('B', 'D', 3)]
Prim算法使用的是节点的概念,每次从已经生成的节点出发,选择一条权值最小的边,并将其与一个新的节点连接。Kruskal算法使用的是边的概念,每次从剩下的边中选择一条最小的边,并将其与已经选择的边进行比较,如果不会形成环,就选择这条边。两种算法均能得到最小生成树。