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;
}
输入输出信息: