KruskalKruskal算法:从权值最小的边出发着手构建最小生成树的。其基本过程是:先对图中的所有边按照权重值从小到大进行排序,然后着手选取边构建最小生成树。如果直接从小到大按顺序选取,有可能形成了环,所以对环的处理就成了核心问题。

图解

还是以上一篇PrimPrim算法中的例子来一个图解,这样更能理解一点

图论 (六) 最小生成树算法【Kruskal算法】
(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) 按权重依次从小到大将边加入最小生成树。

图论 (六) 最小生成树算法【Kruskal算法】

我们依次将边ei(7),ch(8),ab(10),af(11),bg(12),bh(12),di(16)e-i(7),c-h(8),a-b(10),a-f(11),b-g(12),b-h(12),d-i(16)加入最小生成树中(图中标绿色的边),到目前为止,没有任何问题。

(3) 检查新加入的边是否构成了环。
下面,按照顺序需要将边fg(17)f-g(17)加入最小生成树。
图论 (六) 最小生成树算法【Kruskal算法】

很不幸,这时abgfaa-b-g-f-a构成了环。于是fg(17)f-g(17)被排除,不能被加入最小生成树。

下面按顺序加入的边是bc(18)b-c(18),同样bcgbb-c-g-b也构成了环,所以边bc(18)b-c(18)也被排除。如此依次类推,最后得到的最小生成树是:
图论 (六) 最小生成树算法【Kruskal算法】

上述就是KruskalKruskal算法的基本原理。下面我们用代码来实现一下这个算法

算法实现

首先,根据上面的描述我们得创建一个边集数组,由于有三个变量所有用结构体来实现:

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为边的数量

然后对KruskalKruskal算法描述:
parentparent:用来判断边与边之间是否形成了环

//查找连线顶点的尾部下标
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;
}

input.txtinput.txt

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

outputoutput

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函数由边数ee决定,时间复杂度为O(loge)O(log{e}),而外面有一个forfor循环ee次。所以KruskalKruskal算法的时间复杂度为O(eloge)O(elog{e})

参考文章:
图论(九)最小生成树-Kruskal算法

相关文章: