📅  最后修改于: 2023-12-03 15:42:20.608000             🧑  作者: Mango
本题为Sudo GATE 2021的编程试题的第18题。该题考察了求最小生成树的一个经典算法:Kruskal算法。
给定一个带权无向连通图,试求其最小生成树。你需要实现一个函数:
def MST(n: int, edges: List[Tuple[int, int, int]]) -> List[Tuple[int, int, int]]:
其中,$n$ 表示节点数(节点从 $1$ 到 $n$ 编号),$edges$ 是边的列表,每个元素是一条边 $(u, v, w)$,表示从节点 $u$ 连向节点 $v$ 的一条边,权值为 $w$。
函数的返回值是一个列表,包含最小生成树中的所有边,每个元素仍为 $(u, v, w)$ 的形式。
如 $n=4, edges=[(1, 2, 1), (1, 3, 2), (3, 4, 3), (2, 4, 4)]$,则应该返回 $[(1, 2, 1), (1, 3, 2), (3, 4, 3)]$。
Kruskal算法是经典的最小生成树算法之一。其算法思路如下:
为了实现第2步,我们需要使用图的并查集数据结构,来判断加入某个边是否会形成环。
下面给出完整的Kruskal算法的实现,时间复杂度为 $O(m\log m)$,其中 $m$ 是边数,即 $len(edges)$。其中 sort_edges()
函数对边的列表进行排序,find_parent()
函数实现并查集查询,union()
函数实现并查集合并。
from typing import List, Tuple
def MST(n: int, edges: List[Tuple[int, int, int]]) -> List[Tuple[int, int, int]]:
def sort_edges():
edges.sort(key=lambda edge: edge[2]) # 按边的权值排序
def find_parent(node):
if node != parents[node]:
parents[node] = find_parent(parents[node])
return parents[node]
def union(edge):
parent1 = find_parent(edge[0])
parent2 = find_parent(edge[1])
if parent1 != parent2:
parents[parent2] = parent1
return True
return False
# 初始化并查集
parents = list(range(n + 1))
# 按权值排序
sort_edges()
# 依次检查边是否形成环,并加入生成树
tree_edges = []
for edge in edges:
if union(edge):
tree_edges.append(edge)
if len(tree_edges) == n - 1: # 如果已经包括了所有节点,则退出
break
return tree_edges
我们可以使用下面的测试样例来进行测试:
assert MST(4, [(1, 2, 1), (1, 3, 2), (3, 4, 3), (2, 4, 4)]) == [(1, 2, 1), (1, 3, 2), (3, 4, 3)]
assert MST(5, [(1, 2, 3), (2, 3, 2), (3, 4, 5), (4, 5, 1)]) == [(4, 5, 1), (2, 3, 2), (1, 2, 3), (3, 4, 5)]
其中第一个样例给出的边是上面的例子,第二个样例是另一个简单的例子。通过这两个测试样例,我们可以检查实现是否正确。