📜  MST 的 Prim 算法和 Kruskal 算法之间的区别(1)

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

MST 的 Prim 算法和 Kruskal 算法之间的区别

在图论中,最小生成树(Minimum Spanning Tree,MST)是指一个连接所有点的树中,边权值之和最小的树。在实际问题中,MST的应用非常广泛,例如:通信网络、电力磁网、航线规划等领域。目前,常用的MST算法有Prim算法和Kruskal算法,接下来将介绍这两种算法的区别。

Prim算法

Prim 算法是一种贪心算法,它采用了广度优先搜索策略。该算法维护两个集合:一个是已经放置在当前最小生成树中的节点集合,另一个则是还未被选择的节点集合。该算法开始时令已经放置在当前最小生成树集合中只有一个初始节点,因此仍然存在N - 1个节点未被选择。然后通过贪心策略在节点集合中选择一个离当前最小生成树最近的节点,把它加入最小生成树集合中。反复执行该过程,直到所有节点都被选入最小生成树集合,则得到最小生成树。

算法步骤:
  1. 在开始时需要选择初始节点,把它加入已选节点集合中,并把它放入候选节点集合中。
  2. 当候选节点集合非空时,从其中选择一个到当前最小生成树距离最近的节点,并把它加入已选节点集合中。
  3. 重复步骤2,直到所有节点都被选入最小生成树集合中。
代码实现:
def Prim(G, s):
    """
    :param G: 图的邻接矩阵表示
    :param s: 起点
    :return: 最小生成树的权值和
    """
    n = len(G)
    dist = [float("inf")] * n
    visited = [False] * n
    dist[s] = 0
    for _ in range(n):
        u = min(filter(lambda x: not visited[x], range(n)), key=dist.__getitem__)
        visited[u] = True
        for v in range(n):
            if not visited[v] and G[u][v] < dist[v]:
                dist[v] = G[u][v]
    return sum(dist)
Kruskal算法

Kruskal算法是一种贪心算法,它采用了并查集来实现。该算法先把图中的边按照权值从小到大排序,然后从小到大扫描每一条边。对于每一条边,如果它所连接的两个节点不在同一个并查集中,就把它们合并到同一个并查集中,并把它加入最小生成树的集合中。重复执行该过程,直到所有节点都被选入最小生成树集合,则得到最小生成树。

算法步骤:
  1. 把图中的边按照权值从小到大排序。
  2. 初始化并查集,每个节点自成一个集合。
  3. 从小到大扫描每一条边,如果它所连接的两个节点不在同一个并查集中,就把它们合并到同一个并查集中,并把它加入最小生成树的集合中。
  4. 重复步骤3,直到所有节点都被选入最小生成树集合中。
代码实现:
def Kruskal(G):
    """
    :param G: 图的邻接矩阵表示
    :return: 最小生成树的权值和
    """
    n = len(G)
    edges = [(i, j, G[i][j]) for i in range(n) for j in range(i) if G[i][j] != float("inf")]
    edges.sort(key=lambda x: x[2])
    uf = UnionFind(n)
    res = 0
    for u, v, w in edges:
        if uf.find(u) != uf.find(v):
            uf.union(u, v)
            res += w
    return res
区别
  • Prim算法的时间复杂度为$O(N^{2})$,Kruskal算法的时间复杂度为$O(E\log{E})$(E为边数),当图是一个稀疏图时,Kruskal算法比Prim算法更适合;
  • Prim算法适用于起点选择不敏感的情况,Kruskal算法则基本不受起点的影响;
  • Kruskal算法是基于边来找解的,很容易使用并查集来实现,Prim算法则是基于点来找解的,需要用到优先队列;

综上所述,两种算法有各自的适用场景,程序员需要选择合适的算法来解决问题。