最小生成樹——克魯斯卡爾算法(Kruskal算法)

克魯斯卡爾算法(Kruskal算法)

對於n個頂點的連通圖而言,其生成的最小生成樹有n-1條邊,即可以保證從任一點出發可以到達任一點且不產生迴路。

克魯斯卡爾算法(Kruskal算法):對每條邊的權值進行從小到大排序,然後從小到大取權值最小的邊,如取出的邊會在樹中產生迴路則捨去,取下一條;若不會產生迴路則加入到樹中。
因此Kruskal算法的關鍵問題就是:如何判斷新加入的邊是否會產生迴路

判斷是否會產生迴路的方法爲:在初始狀態下給每個頂點賦予不同的標記,對於遍歷過程的每條邊,其都有兩個頂點,判斷這兩個頂點的標記是否一致,如果一致,說明它們本身就處在一棵樹中,如果繼續連接就會產生迴路;如果不一致,說明它們之間還沒有任何關係,可以連接,同時連接後會將其頂點標記改變。

克魯斯卡爾算法(Kruskal算法)詳細介紹

變化過程

以下圖表示每個頂點標記狀態的變化過程,兩點顏色相同表示在同一邊上,多點顏色相同表示在樹上。
在這裏插入圖片描述
最開始各點標記都不同
水電費水電費
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述

實現方法:

建立兩個結構體數組,一個用於存儲邊的信息edge edges,一個用於記錄各個點的標記assist assists

typedef struct edge{
	VertexType initial; //起點
	VertexType end;//終點
	VertexType weight;//權值
}edge[MAX_VERtEX_NUM];
//定義輔助數組
typedef struct {
	VertexType value;//頂點數據
	int sign;//每個頂點所屬的集合
}assist[MAX_VERtEX_NUM];

對edges進行升序排序,爲方便觀察用字母來表示起始點(實際存儲的是int)。下面是edges(左)和 assists(右)的信息
在這裏插入圖片描述
當AC構成一條邊時,判斷是否構成迴路,因爲AC的標記不同,所以成功。 信息C的標記就變爲A的標記,同時AC這條邊就相當於在樹中存在了。下次就會從edges中的DF進行判斷,更新兩個數組的信息
在這裏插入圖片描述
下次判斷DF,成功,F的標記改爲D的標記
在這裏插入圖片描述
同樣BE
在這裏插入圖片描述
CF,判斷成功,注意這時不僅需要把F變爲C的標記,同時F後面連接的所有是樹中的點都要改爲C的標記,本例中FD都要改變
在這裏插入圖片描述
AD標記相同,不成功,判斷BC,成功。至此一共產生5條邊,對於6個頂點的最少生成樹,已經找出了滿足其條件的5條邊了。
在這裏插入圖片描述
總結:對於新的樹中的邊,把終點及其所連接的所有是樹中點的標記改爲起始點的標記
如X——Y是新的邊,x爲起點,y爲終點
在這裏插入圖片描述
連接後各點標記會改爲
在這裏插入圖片描述

int main(){

	int arcnum, vexnum;
	edge edges;//邊信息的數組
	CreateUDN(&edges, &vexnum, &arcnum);
	//對連通網中的所有邊進行升序排序,結果仍保存在edges數組中 使用的是庫函數
	qsort(edges, arcnum, sizeof(edges[0]), cmp);
	//創建一個空的結構體數組,用於存放最小生成樹
	edge minTree;
	//設置一個用於記錄最小生成樹中邊的數量的常量
	int num = 0;
	//遍歷所有的邊
	for (int i = 0; i<arcnum; i++) {
		//找到邊的起始頂點和結束頂點在數組assists中的位置
		int initial = Locatevex(vexnum, edges[i].initial);
		int end = Locatevex(vexnum, edges[i].end);
		//如果頂點位置存在且頂點的標記不同,說明不在一個集合中,不會產生迴路
		if (initial != -1 && end != -1 && assists[initial].sign != assists[end].sign) {
			//記錄該邊,作爲最小生成樹的組成部分
			minTree[num] = edges[i];
			//計數+1
			num++;
			//將新加入生成樹的頂點標記全部更改爲一樣的


			for (int k = 0; k<vexnum; k++) {//這一步是關鍵
				if (assists[k].sign == assists[end].sign) {
					assists[k].sign = assists[initial].sign;
				}
			}


			//如果選擇的邊的數量和頂點數相差1,證明最小生成樹已經形成,退出循環
			if (num == vexnum - 1) {
				break;
			}
		}
	}
	//輸出語句
	for (int i = 0; i<vexnum - 1; i++) {
		printf("%d,%d\n", minTree[i].initial, minTree[i].end);
	}
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章