📜  门| Sudo GATE 2021的测验|问题18(1)

📅  最后修改于: 2023-12-03 15:42:20.608000             🧑  作者: Mango

门| Sudo GATE 2021的测验|问题18

本题为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算法是经典的最小生成树算法之一。其算法思路如下:

  1. 将所有边按照权值从小到大排序。
  2. 每次挑选当前未被选择的权值最小的边,并检查将其加入生成树后是否会形成环,如果不会形成环,则加入生成树。
  3. 直到生成树中包含了所有节点为止。

为了实现第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)]

其中第一个样例给出的边是上面的例子,第二个样例是另一个简单的例子。通过这两个测试样例,我们可以检查实现是否正确。