Python中Kruskal算法的实现

17 Mar 2025 | 6 分钟阅读

Kruskal算法用于查找加权无向图的最小生成树。最小生成树是图中所有可能生成树中,边权之和最小的一棵。

该算法通过将图的所有边按权重从小到大排序,然后依次将边添加到最小生成树中,前提是添加该边不会形成环。这个过程一直持续到所有顶点都被连接起来。

Kruskal算法的步骤

以下是Kruskal算法的步骤

  1. 将图中所有边按权重从小到大排序
  2. 创建一个新的空集合来表示最小生成树。
  3. 遍历排序后的边列表中的每一条边。
  4. 检查将边添加到最小生成树是否会形成环。如果不会,则将该边添加到最小生成树中。重复此过程,直到所有顶点都连接起来。

Kruskal算法的时间复杂度为O(E log E),其中E是图中边的数量。这使得它成为查找大型图的最小生成树的一种非常高效的算法。

Python中Kruskal算法的实现

分步实现如下:

  1. 我们定义一个Graph类,它有三个实例变量:vertices,即图中的所有顶点列表;edges,即图中的所有边列表;以及adjacency_list,一个表示图的邻接列表的字典。
  2. 我们定义一个名为add_edge的方法来向图中添加边。此方法接受三个参数:uv,它们是边所连接的顶点;以及weight,即边的权重。我们将边添加到edges列表,并将其添加到adjacency_list中。
  3. 我们定义一个名为find_parent的方法,用于查找并查集(disjoint-set)数据结构中给定顶点i的父节点。图的连通元素是通过并查集数据结构来跟踪的。每个顶点最初都是自己的父节点,我们使用find_parent方法来查找属于i的连通分量的根节点。
  4. 我们定义一个名为union的方法来合并并查集数据结构中的两个连通分量。union方法接受四个参数:parent,一个表示每个顶点父节点的字典;rank,一个表示每个顶点秩(rank)的字典;xy,这是我们要合并其连通分量的顶点。我们使用find_parent方法查找属于x和y的连通分量的根节点,然后根据它们的秩合并这些分量。
  5. 我们定义一个Kruskal方法来实现Kruskal算法。该方法初始化一个空的集合minimum_spanning_tree来存储最小生成树中的边。它还为并查集初始化了一个parent字典和一个rank字典。

代码

输出

{(1, 3, 1), (2, 3, 4), (0, 3, 3)}

输入的图如下:

Implementation of Kruskal's Algorithm in Python
  • Kruskal算法是一种贪心算法,用于查找连通加权图的最小生成树。

这个集合表示输入图的最小生成树中的边。我们可以看到,最小生成树的总权重是1 + 3 + 5 = 9

  • 该算法通过按权重对图的边进行排序,然后从最小权重开始,依次将边添加到最小生成树中。
  • 在将每条边添加到最小生成树时,都会检查以确保它不会形成环。如果这条边连接了已经属于同一连通分量的两个顶点,那么添加这条边就会形成一个环,该边将被丢弃。
  • 只要图是连通的且边权重不重复,Kruskal算法总是能找到图的最小生成树。
  • Kruskal算法的时间复杂度O(E log E),其中E是图中的边的数量。这是因为该算法需要按权重对边进行排序,这需要O(E log E)的时间,然后逐个处理每条边,这需要O(E)的时间。
  • Kruskal算法的空间复杂度O(V + E),其中V是图的顶点的数量E是图的边的数量。这是因为该算法需要存储图的边和邻接列表,以及用于跟踪连通分量的并查集数据结构。
  • Kruskal算法可以使用优先队列来实现,以更有效地按权重对边进行排序。这会将算法的时间复杂度提高到O(E log V),其中V是图的顶点数量。然而,空间复杂度仍为O(V + E)
  • 在某些情况下,给定的图可能存在多个最小生成树。Kruskal算法会找到其中一棵,但不一定是全部。

Kruskal算法常用于网络设计问题,例如设计通信网络或电力网。该算法也可以在python中使用优先队列来实现。

代码

输出

(2, 3, 1)
(0, 2, 3)
(0, 1, 2)
(1, 4, 3)

这些是上述相同图的最小生成树中的边。

在此实现中,我们使用优先队列来按权重排序边。我们将每条边及其权重作为优先级添加到优先队列中,这样当我们从队列中检索边时,总是先得到权重最小的边。之后,我们遍历优先队列中的边,只要它们不形成环,就逐一将它们添加到最小生成树中。我们使用并查集数据结构的findunion方法来跟踪图的连通分量,就像在之前的实现中一样。

要使用此实现,您可以创建一个Graph对象,使用add_edge方法向其添加边,然后调用kruskal方法来查找最小生成树。