C++实现kruskal算法

1,基本思想

假设共有n个节点,m条边,每次从未选择过的边集E中选取权值最小的一条边,若该最小边的端点不与已选边的端点构成回路,则将该边加入到最小生成树中,重复这一步骤直到选够n-1条边或无边可选。举例如下:
在这里插入图片描述
图中共有9条边,
第一次:选取最小一条边,权值为1,节点v1和v3被标注;
在这里插入图片描述

第二次:选取最小一条边,权值为2,节点v1和v4被标注;
在这里插入图片描述

第三次:选取最小一条边,权值为3,节点v1和v5被标注;
在这里插入图片描述

第四次:选取最小一条边,权值为3,节点v2和v4被标注;
在这里插入图片描述

选取的边达到n-1,结束;

2,注意点

这里的注意点主要是如何在选取过程中判断两点是否构成回路,看了其他作者的判断方法,思路都大致相同(开始老是看不懂,后来终于明白了,还是因为太懒不愿意举个例子走一遍),就是判断两点的父节点(这里用父节点不太准确,只是个大致意思)是否相同,若相同,则会构成回路,该边不可取,若不同,则该边可取。

3,代码实现

#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;

struct edge
{
    
    
	int u, v;
	int weight;
};
vector<int> father; //记录每个节点的父亲
vector<int> result; //存储最后获得的各条边

bool compare(edge a, edge b)
{
    
    
	return a.weight < b.weight;
}

int findfather(int a)
{
    
    
	while (a != father[a])
	{
    
    
		a = father[a];
	}
	return a;
}
void kruskal(int n, vector<edge> Edge)
{
    
    
	father.resize(n);
	sort(Edge.begin(), Edge.end(), compare);
	for (int i = 0; i < n; ++i)
	{
    
    
		father[i] = i;
	}
	for (int i = 0; i < Edge.size() && result.size() < n-1; ++i)
	{
    
    
		int u = Edge[i].u;
		int v = Edge[i].v;
		if (findfather(u) != findfather(v)) //判断父节点是否相同
		{
    
    
			result.push_back(Edge[i].weight);
			father[findfather(u)] = father[findfather(v)]; //将两点并入一个集合中
		}
	}
	if (result.size() != n - 1)
	{
    
    
		cout <<  result.size()  << "该图不连通" << endl;
		return;
	}
	else
	{
    
    
		cout << "最小生成树的各边如下:" << endl;
		for (int i = 0; i < result.size(); ++i)
		{
    
    
			cout << result[i] << endl;
		}
	}
}

int main()
{
    
    
	int n, m;
	cout << "输入结点数:";
	cin >> n;
	cout << "输入边数:";
	cin >> m;
	vector<edge> Edge(m);
	cout << "输入各条边的信息:" << endl;
	for (int i = 0; i < m; ++i)
	{
    
    
		cin >> Edge[i].u >> Edge[i].v >> Edge[i].weight;
	}
	kruskal(n,Edge);
	return 0;
}

输入输出信息:在这里插入图片描述
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_46027243/article/details/108903948