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

版权声明:本文为博主原创文章,转载请注明出处-- https://blog.csdn.net/qq_38790716/article/details/86645076

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

图解

还是以上一篇 P r i m Prim 算法中的例子来一个图解,这样更能理解一点

在这里插入图片描述
(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) 按权重依次从小到大将边加入最小生成树。

在这里插入图片描述

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

(3) 检查新加入的边是否构成了环。
下面,按照顺序需要将边 f g ( 17 ) f-g(17) 加入最小生成树。
在这里插入图片描述

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

下面按顺序加入的边是 b c ( 18 ) b-c(18) ,同样 b c g b b-c-g-b 也构成了环,所以边 b c ( 18 ) b-c(18) 也被排除。如此依次类推,最后得到的最小生成树是:
在这里插入图片描述

上述就是 K r u s k a l Kruskal 算法的基本原理。下面我们用代码来实现一下这个算法

算法实现

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

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

然后对 K r u s k a l Kruskal 算法描述:
p a r e n t parent :用来判断边与边之间是否形成了环

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

i n p u t . t x t input.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

o u t p u t output

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函数由边数 e e 决定,时间复杂度为 O ( l o g e ) O(log{e}) ,而外面有一个 f o r for 循环 e e 次。所以 K r u s k a l Kruskal 算法的时间复杂度为 O ( e l o g e ) O(elog{e})

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

猜你喜欢

转载自blog.csdn.net/qq_38790716/article/details/86645076