常用的两种求最小生成树的算法是Prim 算法和Kruskal 算法,这里对个算法做一个总结。
Prim算法:只可以用于连通图,当图中存在不连通的点时,此算法不适用。 Kruskal算法:可以用于非连通图,这种情况下求出来的是各个子连通图的最小生成树。
Prim算法 算法描述 Prim算法和Dijkstra算法很相似: 1.初始化一个空集合和一个记录数组,并从一个顶点出发,将这个顶点加入集合中; 2.然后寻找与这个顶点之间距离最近(权值最低)的且没有在集合中的点,将寻找到的点加入集合,并使用这个点的数据对记录数组进行更新; 3.以新加入的点作为顶点重复步骤2,直到所有的顶点都在集合中。
不同之处在于,Dijstra算法中记录数组记录的是其余点到出发顶点的最短距离,而Prim算法中记录数组记录的是其余点到集合中任意点的最短距离
代码实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 #include <iostream> #define INF 999999 using namespace std;int dic[1001 ][1001 ];int D[1001 ];int visit[1001 ];void joinEdge (int e1, int e2, int v) { dic[e1][e2] = v; dic[e2][e1] = v; } int main () { int n, m, ans = 0 ; int e1, e2, v; cin >> n >> m; for (int i = 0 ; i < n; ++i) { D[i] = INF; for (int j = i+1 ; j < n; ++j) { if (i == j) dic[i][i] = 0 ; else { dic[i][j] = INF; dic[j][i] = INF; } } } D[0 ] = 0 ; while (m--) { cin >> e1 >> e2 >> v; --e1; --e2; joinEdge (e1, e2, v); } int minEdges = INF; int minNode; for (int i = 0 ; i < n; ++i) { for (int j = 0 ; j < n; ++j) { if (!visit[j] && D[j] < minEdges) { minEdges = D[j]; minNode = j; } } visit[minNode] = 1 ; ans += minEdges; for (int j = 1 ; j < n; ++j) { if (!visit[j] && dic[minNode][j] < D[j]) D[j] = dic[minNode][j]; } minEdges = INF; } cout << "最小生成树边的权值和为:" << ans << " " << endl; return 0 ; }
Kruskal算法 算法描述 不同于Prim算法,Kruskal算法是从边 出发的。在这里使用并查集 能较简单的实现Kruskal算法。首先初始化一个大小为顶点数的并查集,然后对图中的所有边按升序排序。(如果需要判断连通分支数量,可以初始化一个数值为顶点数目的变量)从权值最小的边开始,遍历所有的边。 如果某条边的两个顶点不在同一个集合中,那么就可以将这两个顶点加入同一个集合,并且这条边就是最小生成树中的一条,(然后对记录连通分支数量的变量减一) 如果某条边的两个顶点在同一个集合中,那么跳过这条边,继续遍历后面的边。
例题描述 对有n个顶点、m条边的无向图,计算其最小生成树并输出树的权重,即构成最小生成树的所有边的权重值总和。如果无向图连通,计算结果是一个含(n−1)条边的最小生成树;如果图不连通,则依次计算各个连通子图的最小生成树,再将所有树的权重相加。
输入格式: 第一行给出正整数n(≤1000)和m(≤10000),分别表示无向图的顶点和边的数量。接下来m行数据,每行给出三个非负整数,分别表示两个顶点编号以及这两个顶点之间边的权值。
输出格式: 在一行中输出两个整数,用空格分开。第一个整数表示无向图的最小生成树的权重,第二个整数是图中连通子图的数量(如果图连通,则输出1)。
输入样例1:
5 5
0 1 1
1 2 1
2 3 1
3 4 1
0 4 2
输出样例1:
4 1
输入样例2:
5 2
0 1 1
2 3 1
输出样例2:
2 3
代码实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 #include <iostream> #include <map> #include <vector> using namespace std;map<int , vector<pair<int , int >>> edges; class Union { vector<int > pre; public : Union (int n) { pre.resize (n); for (int i=0 ; i<n; ++i) pre[i] = i; } int search (int node) { int temp, root; root = node; while (root != pre[root]) root = pre[root]; while (node != root) { temp = pre[node]; pre[node] = root; node = temp; } return root; } void join (int x, int y) { int root1 = search (x); int root2 = search (y); if (root1 != root2) pre[root2] = root1; } bool isConnect (int x, int y) { return search (x) == search (y); } }; int main () { int n, m, ans = 0 ; int e1, e2, v; cin >> n >> m; Union set (n) ; while (m--) { cin >> e1 >> e2 >> v; edges[v].push_back (make_pair (e1, e2)); } for (auto i = edges.begin (); i != edges.end (); ++i) { for (int j = 0 ; j < (*i).second.size (); ++j) { int x = (*i).second[j].first, y = (*i).second[j].second; if (!set.isConnect (x, y)) { set.join (x, y); ans += (*i).first; --n; } } } cout << ans << " " << n << endl; return 0 ; }