非常尷尬,我之前寫的那篇寫克魯斯卡爾算法的那篇博客思路是錯誤的,我當時也不知道是被哪篇博客誤導了,汗。
所以我在這裏重新寫一篇正確的,而且事先聲明,我沒有使用並查集,我只是使用了一個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算法所得的一致。