推免複習之數據結構與算法 Kruskal算法(最小生成樹)

非常尷尬,我之前寫的那篇寫克魯斯卡爾算法的那篇博客思路是錯誤的,我當時也不知道是被哪篇博客誤導了,汗。

所以我在這裏重新寫一篇正確的,而且事先聲明,我沒有使用並查集,我只是使用了一個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算法所得的一致。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章