最小生成樹(MinSpanTree)的Kruskal演算法

CJYBQ發表於2020-10-05

分析

最小生成樹的克魯斯卡爾(Kruskal)演算法實現主要是通過將輸入的圖的邊存在優先佇列中,然後依次從優先佇列中取最小的邊加入生成樹,最小生成樹的邊的個數是頂點數減一,因此退出條件是while (!pq.empty() || cnt < n - 1),其中需要注意的是在定義邊結點EdgeNode時需要過載運算子<,因為在優先佇列priority_queue中的實現邏輯就是需要比較元素的大小,那麼在自定義結構體中則需要過載該運算子。
另外一個知識點就是並查集,這個資料結構的實現方式比較簡單,類似於樹的實現方式,具體知識可參考資料。總的來說,Kruskal演算法的實現還是相對簡單的。

C/C++程式碼實現

/*******************************************************
*              Kruskal演算法實現最小生成樹
********************************************************/
#include<iostream>
#include<queue>
using namespace std;

const int maxn = 100;
int prev_[maxn];
int vexs[maxn];
int n;//頂點數
int m;//邊數


struct EdgeNode {
	int u, v;// u -> v
	int weight;//邊權值
	/*對於常量型成員變數和引用型成員變數  此處成員變數都是普通成員變數 哪種方法初始化都行
	必須通過初始化化列表的方式進行初始化
	在建構函式體內進行賦值的方式是行不通的*/
	EdgeNode(const int& u, const int& v, const int& weight) :u(u), v(v), weight(weight) {}//建構函式
	bool operator < (const EdgeNode& en) const {
		return  en.weight < this->weight; //最小值優先  建立堆元素升序排列
	}
};

class UnionFind {
public:
	UnionFind(const int& n);
	int Find(const int& p);
	void Union(const int& p, const int& q);
};
UnionFind::UnionFind(const int& n)
{
	for (int i = 0; i < n; i++)
	{
		prev_[i] = i;//父節點初始化為自己
	}
}

void UnionFind::Union(const int& p, const int& q)
{
	int x = Find(p); //找到p的祖先為x
	int y = Find(q); //找到q的祖先為y
	if (x != y)//不屬於同一組
	{
		prev_[y] = x; //p, q 合併到一組
	}
}

int UnionFind::Find(const int& p)
{
	if (prev_[p]  == p)
		return p;//找到祖先
	return Find(prev_[p]);
}

int LocateVertex(int v)
{
	for (int i = 0; i < n; i++)
	{
		if (v == vexs[i])
		{
			return i;
		}
	}
	return -1;
}

void MinSpanTree_Kruskal(priority_queue<EdgeNode>& pq)
{
	int cnt = 0;
	UnionFind UF(n);
	while (!pq.empty() || cnt < n - 1)//MST的邊數為 頂點數 - 1
	{

		EdgeNode en = pq.top();
		pq.pop();

		int u = LocateVertex(en.u);//定位頂點索引
		int v = LocateVertex(en.v);

		int x = UF.Find(u);
		int y = UF.Find(v);
	
		if (x != y) //兩個頂點不屬於同一組
		{
			UF.Union(x, y);//合併兩頂點
			cnt++; //加入一條邊
			cout << "加入邊(" << vexs[u] << "," << vexs[v] << "); 邊長為" << en.weight << "." << endl;
		}
	}
}

int main()
{
	int u[maxn];//起始點
	int v[maxn];//終止點
	int w[maxn];//權值
	cout << "請輸入頂點數(n >= 2):";
	cin >> n;
	cout << "請輸入邊數(m >= n - 1):";
	cin >> m;
	cout << "請輸入邊依附的頂點及權值資訊:" << endl;

	for (int i = 0; i < n; i++)
	{
		vexs[i] = i + 1;
	}
	priority_queue<EdgeNode> pq;
	for (int i = 0; i < m; i++)
	{
		cin >> u[i] >> v[i] >> w[i];
		EdgeNode en(u[i], v[i], w[i]);
		pq.push(en);//建立小頂堆 邊按權值進行升序排列
	}

	MinSpanTree_Kruskal(pq);

	return 0;
}

輸出結果

請輸入頂點數(n >= 2):6
請輸入邊數(m >= n - 1):10
請輸入邊依附的頂點及權值資訊:
1 2 6
1 3 1
1 4 5
2 3 5
2 5 3
3 4 5
3 5 6
5 6 6
3 6 4
4 6 2
加入邊(1,3); 邊長為1.
加入邊(4,6); 邊長為2.
加入邊(2,5); 邊長為3.
加入邊(3,6); 邊長為4.
加入邊(2,3); 邊長為5.

相關文章