直观理解:最小生成树算法Prime和Kruskal

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了直观理解:最小生成树算法Prime和Kruskal相关的知识,希望对你有一定的参考价值。

参考技术A

  在正式讲解Prime和Kruskal算法之前,我们需要先搞明白什么是最小生成树,而搞明白最小生成树的概念之前,我们先介绍几个名词。
联通图: 在一个无向图 中,若任意两个顶点 与 之间都有最少一条路径相通,则称该无向图 为连通图。
生成树: 如果一个联通图 联通子图 包含图中所有的顶点,且任意任意两顶点之间有且仅有一条路径,则称该联通子图为联通图 的生成树;
最小生成树: 连通图 的所有生成树中,边的权重之和最小的生成树称为最小生成树。
解释完上述名词之和,接下来我们重点介绍两种经典的最小生成树算法Prime算法和Kruskal算法。

  假设存在联通图 ,图中所有的顶点集合为 ,集合 表示已经加入到生成树中的顶点集合,集合 表示未加入到生成树中的顶点集合。一开始,随机指定一个顶点 加入到集合 中,则 ,每次从集合 与集合 的顶点所构成的所有边中选取权值最小的一条边 作为生成树的边,并将边 在集合 的那个顶点加入到集合 中,如此下去直到集合 中的全部顶点都加入到集合集合 中,得到最小生成树。算法的执行过程如下图所示:

  将联通图 中所有的边按其权值由小到大的次序进行排序,对边的权值按从小到大的顺序进行选取,若该边选取后与已选择的边无法形成回路,则保留当前边,否则跳过当前边,选择下一条。依次选够 条边即可得到最小生成树,其中 为连通图 中的顶点个数。算法的执行过程如下图所示:

   1. 执行Kruskal算法的 图存储结构 一般采用边集数组的方式进行存储,且权值相等的边在数组中排列的排列先后顺序并不影响最终最小生成树的权值总和,但可能会影响最小生成树的形状。由于需要对边进行排序和选择,因此该方法对于边相对比较多的图,运行时间较长;
   2. 执行普里姆算法的图存储结构一般采用邻接矩阵的方式。该方法以某个顶点为出发点,每次选择两个集合之间权值最小的边进行最小生成树生成,较适合边较多的图的最小生成树生成,且性能较好。

最小生成树算法Kruskal详解

要讲Kruskal,我们先来看下面一组样例。

4 5
1 2 3
1 4 5
2 4 7
2 3 6
3 4 8

 

14

  

技术分享图片

画出来更直观一些,就是上面的这张图。

智商只要不是0的(了解最小生成树是什么的童鞋)应该都知道要选择1<->4, 1<->2, 2<->3三条边那么大家就会问为什么选择这三条呢。

一棵树边的数量等于这棵输的点的数量减1。(不信自己画画试试)这里不再解释。

假设有N个点,那么我们肯定要选择n-1条边来生成一棵树。这棵树就是这张图的生成树。

很显然生成树有一堆。

而最小生成树是指边权之和最小的生成树。那么我们自然而然的想到了贪心!!!

没错,就是贪心,我们按照边的权值进行贪心。

同时为了保证到最后所有的点都连在了一起,并且没有多余的边。我们就要用到并查集。只要两个顶点被一条边所连接。那么他们就有一个共同的祖先。我们在以后就只要判断一下,只要两个顶点没有共同祖先,就可以将他们连入生成树中。当边的条数加到n-1时,我们就可以结束了。

下面是代码

#include <iostream>
#include <cstdio>
#include <algorithm>

using namespace std;

int n, m;
int f[100008];
int sum, tot;
struct edge{
    int u, v, w;
}ed[1000008];

bool cmp(edge a, edge b) {
    return a.w < b.w;
}

int find(int x) {
    if(x == f[x]) return x;
    else return f[x] = find(f[x]);
}

int main() {
    scanf("%d%d", &n, &m);
    for(int i=1; i<=n; i++) {
        f[i] = i;
    }
    for(int i=1; i<=m; i++) {
        scanf("%d%d%d", &ed[i].u, &ed[i].v, &ed[i].w);
    }
    sort(ed+1, ed+1+m, cmp);
    for(int i=1; i<=m; i++) {
        int xx = find(ed[i].u), yy = find(ed[i].v);
        if(xx != yy) {
            f[xx] = find(yy);
            tot++;
            sum += ed[i].w;
        }
        if(tot == n-1) {
            break;
        }
    }
    printf("%d", sum);
}
作者:wlz
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

以上是关于直观理解:最小生成树算法Prime和Kruskal的主要内容,如果未能解决你的问题,请参考以下文章

最小生成树 prime kruskal

最小生成树

最小生成树(MST) prime() 算法 kruskal()算法 A - 还是畅通工程

关于最小生成树(并查集)prime和kruskal

数据结构:最小生成树--Kruskal算法

最小生成树算法Kruskal详解