最小生成樹(MST,kruskal算法)

前言

最小生成樹,對於一個無向圖聯通且不含圈的圖稱爲樹
給定無向圖G=(V,E),連接G中所有點,且邊集是E的子集的數稱爲G的生成樹(Spanning Tree),而權值最小的生成樹稱爲最小生成樹(Minimal Spanning Tree, MST)。構造MST的算法有很多,最常見的有兩個:Kruskal算法和Prim算法。這裏只介紹Kruskal算法,因爲這個算法編寫容易且效率很高。

前面描述不清楚的話,可以看下圖
在這裏插入圖片描述
這是一個無向圖,每條邊有權重,你可以採用幾條邊就能連接所有的點,就得到了一顆生成樹(前提是不含圈)。如果樹的所有邊的權值之和最小,就稱爲最小生成樹。

算法流程

Kruskal算法的流程非常簡單和直接:

  • 1、對於邊集E,對所有的邊按照權值從小到大排序
  • 2、 依次將排序好的邊放入MST中,如果加入這條邊(u,v)使圖出現了環,則放棄
  • 3、直到所有的點都出現在MST中,構造完成。

其中第2步可能出現環,則需要檢驗環,這裏可以採用DFS或者BFS的方式進行圖遍歷(寫起來很複雜,需要判斷什麼樣纔算是環,而且複雜度很高)。所以我們採用另外一種方式來進行檢驗,那就是"並查集"。並查集可以查看我之前的博文並查集解釋

另外第2步會出現的情況就是已經有幾個邊入選但是構不成樹,如下圖所示:
在這裏插入圖片描述
事實上我們並不需要馬上構成樹,我們只需要將邊選出來,然後構造點的集合就可以了,這也就是爲什麼要用並查集的另一個理由,例如上圖中有三個集合,分別是{4,6},{2,5},{1,3,}。如果連同了,那兩個集合就合併爲一個集合。其他還沒有邊連接的點構成另外的集合,例如{7},{8},{9}…

那並查集如何判斷環呢?其實只要判斷邊的兩個端點(u,v)是否在同一個集合中,如果在同一個集合中,說明已經聯通(構成樹),你再添加就會構成環。
以上,kruskal算法應該解釋清楚了。

C++模板

struct Edge{
	int from, to, cost;
	Edge(int from, int to, int cost){this->from = from; this->to = to; this->cost = cost;};
}; // 邊的結構體,其他可能有不同
bool cmp(Edge e1, Edge e2){return e1->cost<e2->cost;} // 比較函數
//以下是並查集
int pre[N]; //記錄每個點的父節點, N個節點
int unionsearch(int root) 
{
	int son, tmp;
	son = root;
	while(root != pre[root]) //尋找根節點
		root = pre[root];
	while(son != root) //已經找到了根節點,進行路徑壓縮
	{
		tmp = pre[son];
		pre[son] = root;
		son = tmp;
	}
	return root; //返回根節點
}
void join(int root1, int root2) //合併函數
{
	int x, y;
	x = unionsearch(root1);//找到根節點
	y = unionsearch(root2);//找到根節點
	if(x != y) 
		pre[x] = y; //合併,就是其中一個的父節點換爲另一個
}

Edge e[nmax]; // 默認已經讀取完畢,並且邊的個數就是nmax
int Kruskal()
{
	int total_cost = 0;
	int total_num = 0;
	sort(e, e+nmax, cmp);
	memset(pre, 0, sizeof(pre) * N);
	for(int i = 0; i<nmax;i++)
	{
		int from = e[i]->from;
		int to = e[i]->to;
		int cost = e[i]->cost;
		//節點還沒有加入過
		if(!pre[from]) 
		{
			pre[from] = from;
			total_num++; //節點數增加
		}
		if(!pre[to]) 
		{
			pre[to] = to;
			total_num++; //節點數增加
		}
		if(unionsearch(from) != unionsearch(to))//這條邊兩個點不屬於同一個集合,那就合併
			join(from, to);
		else
			continue;
		total_cost += cost;
		if(total_num==N) //全部加入,退出
			break;
	}
	return total_cost;
}

條件有限就直接記事本敲了,如果有錯誤還請告知

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