非常尴尬,我之前写的那篇写克鲁斯卡尔算法的那篇博客思路是错误的,我当时也不知道是被哪篇博客误导了,汗。
所以我在这里重新写一篇正确的,而且事先声明,我没有使用并查集,我只是使用了一个label数组来记录各个顶点各属于哪个集合,其他与正常求克鲁斯卡尔算法的思路一致。
首先是使用三元组数据表作为存储的数据结构,每条边的数据结构如下所示:
class edge
{
public:
int start;
int end;
int weight;
edge(int s,int e,int w)
{
start = s;
end = e;
weight = w;
}
};
然后使用stl中的快排对权值进行升序排序 ,对排完序的三元表数组就可以进行克鲁斯卡尔算法了。对这个数组进行从头到尾遍历,如果两个顶点分别属于不同集合,则添加此边,否则不添加,以避免最小生成树中形成回环。直到遍历完所有边,如果该图是连通图,则生成的一定是最小生成树。
全部的代码如下所示,我添加了注释的部分是对顶点做标记的代码,很重要!
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
class edge
{
public:
int start;
int end;
int weight;
edge(int s,int e,int w)
{
start = s;
end = e;
weight = w;
}
};
bool compare(edge a,edge b)
{
return a.weight < b.weight;
}
void InputGraph(vector<edge> &graph)
{
int n = 0;
cin >> n;
for (int i = 0; i < n; i++)
{
int start = -1, end = -1, weight = 0;
cin >> start >> end >> weight;
graph.push_back(edge(start,end,weight));
}
sort(graph.begin(),graph.end(),compare);
}
int kruskal(vector<edge> &graph)
{
int size = graph.size();
int weight = 0;
vector<int> label(size);
for (int i = 0; i < size; i++) //为每个顶点设置标签,以避免回环
{
label[i] = i;
}
cout << endl;
for (int i = 0; i < size; i++)
{
if (label[graph[i].start]!=label[graph[i].end])
{
int flag = label[graph[i].start];
int toChange = label[graph[i].end];
for (int j = 0; j < size; j++) //把所连接的左连通分支每个顶点都打上右连通分支的记号
{
if (label[j] == flag)
{
label[j] = toChange;
}
}
weight += graph[i].weight;
cout << graph[i].weight << endl;
}
}
return weight;
}
int main()
{
vector<edge> graph;
InputGraph(graph);
cout << kruskal(graph) << endl;
system("pause");
return 0;
}
输入:
8
0 1 2
0 2 3
1 3 7
2 3 5
2 4 3
3 4 4
4 5 2
1 5 8
输出的总权值应该是14,和使用prim算法所得的一致。