Kruskal最小生成树算法

Kruskal算法采用了边贪心的策略,同时运用了并查集的知识。其中为了实现kruskal算法,我们采用对边进行存储的图,对边进行从小到大排序、运用并查集等相关技巧,尽可能降低时间复杂度达到实现算法的目的。

体会:在算法的设计过程中,我们采用哪种数据结构进行存储、借助哪种数据结构工具、如何实现的思想,都会导致不同的时间复杂度,因此,我们力求借助最合理的数据结构、借助以前的一些学过的算法知识和思想去设计一个算法,做到把时间复杂度降到最低,当把一个算法写出来后,应该回头看看,此算法时间复杂度还有没有优化的空间?导致你这个算法时间复杂度过高的原因有哪些?有哪些是算法做了很多无用功而导致时间复杂度过高,所以,你应该怎么进行优化呢?

#include"stdafx.h"
#include<cstdio>
#include<vector>
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
const int maxn = 500;
const int INF = 1e9;
int Vertexnum, Edgenum, Start;//顶点数、边数、起点
struct node {//边的结构体
	int u, v;
	int weight;
};
node G[maxn];//(边)存储图
bool vis[maxn] = { false };//标记顶点v是否被加入生成树中
int addvnum = 0;//加入生成树顶点的数量
bool cmp(node a, node b) {
	return a.weight < b.weight;
}
int father[maxn];//并查集
void init(int n) {//并查集的初始化操作
	for (int i = 0; i < n; i++) {
		father[i] = i;
	}
}
int findfather(int x) {//查找x所在集合的根结点
	while (father[x] != x) {
		x = father[x];
	}
	return x;
}
int Kruskal() {
	int add_edgenum = 0;//加入的边个数
	int sumweight = 0;//总边权
	init(Vertexnum);//并查集的初始化
	sort(G, G + Edgenum, cmp);//将边按照从小到大排列
	for (int i = 0; i < Edgenum; i++) {//从小到大枚举各边
		int fau = findfather(G[i].u);//顶点u所在集合的根结点
		int fav = findfather(G[i].v);//顶点v所在集合的根结点
		if (fau!=fav) {//如果边的两个顶点不在一个集合
			father[fav] = fau;//将顶点u所在的集合并入到顶点v所在的集合(即将边加入到生成树中)
			sumweight += G[i].weight;
			add_edgenum++;//加入的边数加一
		}
		if (add_edgenum == Vertexnum - 1) break;
	}
	return sumweight;
}
int main() {
	cin >> Vertexnum >> Edgenum;
	int start, end, weight;//起点、终点、边权
	for (int i = 0; i < Edgenum; i++) {
		cin >> start >> end >> weight;
		G[i].u = start;
		G[i].v = end;
		G[i].weight = weight;
	}
	cout << Kruskal();//输出最小生成树边权和
	return 0;
}

补充:并查集有三个基本操作,分别为:初始化、查找x所在集合的根结点、将x和y各自所在的集合进行合并。代码如下:

int father[maxn];//并查集
void init(int n) {//并查集的初始化操作
	for (int i = 0; i < n; i++) {
		father[i] = i;
	}
}
int findfather(int x) {//查找x所在集合的根结点
	while (father[x] != x) {
		x = father[x];
	}
	return x;
}
void Union(int x, int y) {
	int fax = findfather(x);
	int fay = findfather(y);
	if (fax != fay) {//如果x、y两个元素属于不同的集合
		father[fax] = fay;//将x所在的集合并入到y所在的集合
	}
}

注意:考虑一种极端情况,如果有很多的元素形成了一条很长的链,那么以上查找函数的效率就会很低,所以有必要对查找函数进行优化,即进行路径压缩,代码如下:

int findfather(int x) {//查找x所在集合的根结点
	int a = x;
	while (x != father[x]) {
		x = father[x];
	}
        //路径压缩
	int temp;
	while (a != father[a]) {
		temp = father[a];
		father[a] = x;
		a = temp;
	}
	return x;
}


猜你喜欢

转载自blog.csdn.net/qq_39034748/article/details/81032083
今日推荐