算法:从权值最小的边出发着手构建最小生成树的。其基本过程是:先对图中的所有边按照权重值从小到大进行排序,然后着手选取边构建最小生成树。如果直接从小到大按顺序选取,有可能形成了环,所以对环的处理就成了核心问题。
图解
还是以上一篇 算法中的例子来一个图解,这样更能理解一点
(1) 首先构建一个边集数组,即对边权值进行排序后得到的结果:
Begin | End | Weight |
---|---|---|
e | i | 7 |
c | h | 8 |
a | b | 10 |
a | f | 11 |
b | g | 12 |
b | h | 12 |
d | i | 16 |
f | g | 17 |
b | d | 18 |
g | i | 19 |
d | e | 20 |
d | h | 21 |
c | d | 22 |
d | g | 24 |
(2) 按权重依次从小到大将边加入最小生成树。
我们依次将边 加入最小生成树中(图中标绿色的边),到目前为止,没有任何问题。
(3) 检查新加入的边是否构成了环。
下面,按照顺序需要将边
加入最小生成树。
很不幸,这时 构成了环。于是 被排除,不能被加入最小生成树。
下面按顺序加入的边是
,同样
也构成了环,所以边
也被排除。如此依次类推,最后得到的最小生成树是:
上述就是 算法的基本原理。下面我们用代码来实现一下这个算法
算法实现
首先,根据上面的描述我们得创建一个边集数组,由于有三个变量所有用结构体来实现:
struct Edge {
int begin;
int end;
int weight;
};
Edge edges[MAXVEX];
接下来需要对这个边集数组进行按权值排序,这里采用sort直接排序:
bool cmp(Edge a, Edge b) {
return a.weight < b.weight;
}
sort(edges, edges + m, cmp); //这个在创建边集数组之后调用,m为边的数量
然后对
算法描述:
:用来判断边与边之间是否形成了环
//查找连线顶点的尾部下标
int Find(int *parent, int f) {
while (parent[f] > 0)
f = parent[f];
return f;
}
void Kruskal(Edge edges[], int n, int m) {//这里就直接使用边集数组了
int sum = 0;
int i,nn,mm;
int parent[MAXVEX];
//初始化
for (i = 0; i < n; ++i) {
parent[i] = 0;
}
for (i = 0; i < m; ++i) {
nn = Find(parent, edges[i].begin); //找连线对应的顶点
mm = Find(parent, edges[i].end);
//不相等说明此边没有与现有生成树形成环路
if (nn != mm) {
parent[nn] = mm;
cout << map[edges[i].begin] << "->" << map[edges[i].end] << "=" << edges[i].weight << endl;
sum += edges[i].weight;
}
}
cout << "Minimum weight sum: " << sum << endl;
}
完整代码:
#include <iostream>
#include <algorithm>
#include <fstream>
using namespace std;
#define MAXVEX 100
struct Edge {
int begin;
int end;
int weight;
};
Edge edges[MAXVEX];
char map[] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i' };
bool cmp(Edge a, Edge b) {
return a.weight < b.weight;
}
int Find(int *parent, int f) {
while (parent[f] > 0)
f = parent[f];
return f;
}
void Kruskal(Edge edges[], int n, int m) {
int sum = 0;
int i,nn,mm;
int parent[MAXVEX];
for (i = 0; i < n; ++i) {
parent[i] = 0;
}
for (i = 0; i < m; ++i) {
nn = Find(parent, edges[i].begin);
mm = Find(parent, edges[i].end);
if (nn != mm) {
parent[nn] = mm;
cout << map[edges[i].begin] << "->" << map[edges[i].end] << "=" << edges[i].weight << endl;
sum += edges[i].weight;
}
}
cout << "Minimum weight sum: " << sum << endl;
}
int main() {
int i,n,m,begin,end,weight;
ifstream in("input.txt");
in >> n >> m;
for (i = 0; i < m; ++i) {
in >> begin >> end >> weight;
edges[i].begin = begin;
edges[i].end = end;
edges[i].weight = weight;
}
sort(edges, edges + m, cmp);
Kruskal(edges, n, m);
return 0;
}
9 15
0 1 10
0 5 11
1 2 18
1 6 12
1 7 12
2 3 22
2 7 8
3 4 20
3 6 24
3 7 21
3 8 16
4 5 26
4 8 7
5 6 17
6 8 19
e->i=7
c->h=8
a->b=10
a->f=11
b->g=12
b->h=12
d->i=16
g->i=19
Minimum weight sum: 95
算法的Find函数由边数 决定,时间复杂度为 ,而外面有一个 循环 次。所以 算法的时间复杂度为
参考文章:
图论(九)最小生成树-Kruskal算法