大話數據結構學習筆記 - 圖的最小生成樹之Kruskal
算法
Kruskal
算法
克魯斯卡爾(Kruskal
)算法,是用來求加權連通圖的最小生成樹的算法
大話數據結構定義
假設 是連通網,則令最小生成樹的初始狀態爲只有
n
個頂點而無邊的非連通圖 。圖中每個頂點自成一個連通分量。在E
中選擇代價最小的邊,若該邊依附的頂點落在T
中不同的連通分量上,則將此邊加入到T
中,否則社區此邊而選擇下一條代價最小的邊,以此類推,直至T
中所有頂點都在同一連通分量上爲止。
基本思想
按照權值從小到大的順序選擇n - 1
條邊,並保證這n - 1
條邊不構成迴路
具體做法
首先構造一個只含n
個頂點的森林,然後依權值從小到大從連通網中選擇邊加入到森林中,並使森林不產生迴路,直至森林變成過一棵樹爲止
Kruskal
算法圖解
以上圖G4
爲例,使用克魯斯卡爾算法進行演示實現最小生成樹,用parent
表示
第零步: 將鄰接矩陣轉換爲邊表數組,並且按權值大小排序
第一步: 將邊 加入最小生成樹中
邊 的權值最小,故將其加入最小生成樹
第二步: 將邊 加入最小生成樹中
上一步操作後, 邊 的權值最小,故將其加入最小生成樹
第三步: 將邊 加入最小生成樹中
上一步操作後, 邊 的權值最小,故將其加入最小生成樹
第四步: 將邊 加入最小生成樹中
上一步操作後,邊 的權值最小, 但邊 會和最小生成樹中的已有邊構成迴路,故跳過。同理,跳過邊
第五步:將邊 加入到最小生成樹中
上一步操作後,邊 的權值最小,故將其加入到最小生成樹中
第六步: 將邊 加入到最小生成樹中
上一步操作後,邊 權值最小, 但會和已有邊構成迴路,跳過。同理跳過邊 。將邊 加入
此時,最小生成樹構造完成,含有的依次爲
Kruskal
算法要點
對圖的所有邊按照權值大小排序
此問題可通過代碼實例理解
將邊添加到最小生成樹中,如何判斷是否形成迴路
通過記錄每個頂點在最小生成樹中的終點。終點即在最小生成樹中與它連通的最大頂點。每次添加一條邊到最小生成樹中時,判斷該邊的兩個頂點的終點是否重合,重合則構成迴路。
在將 加入到最小生成樹中後,這幾條邊的頂點就都有了終點
C
的終點是F
D
的終點是F
E
的終點是F
F
的終點是F
關於終點,就是將所有頂點按照從小到大的順序排列好之後;某個頂點的終點就是”與它連通的最大頂點”。 雖然邊 權值最小,但終點都是
F
, 故會形成迴路
Kruskal
算法代碼
Edge
邊集數組結構
typedef struct
{
int begin;
int end;
int weight;
}Edge;
算法
/* 生成最小生成樹 */
void MiniSpanTree_Kruskal(MGraph G)
{
int i, j, n, m;
int k = 0;
int parent[MAXVEX]; /* 定義一數組用來判斷邊與邊是否形成環路 */
Edge edges[MAXEDGE]; /* 定義邊集數組,edge的結構爲begin,end,weight,均爲整型 */
/* 用來構建邊集數組並排序********************* */
for(i = 0; i < G.numVertexes - 1; i++)
{
for(j = i + 1; j < G.numVertexes; j++)
{
if(G.arc[i][j] < INF)
{
edges[k].begin = i;
edges[k].end = j;
edges[k].weight = G.arc[i][j];
k++;
}
}
}
sort(edges, &G);
/* ******************************************* */
printf("打印最小生成樹:\n");
for(i = 0; i < G.numVertexes; i++)
parent[i] = 0; /* 初始化數組值爲0 */
for(i = 0; i < G.numEdges; i++) /* 循環每一條邊 */
{
n = Find(parent, edges[i].begin);
m = Find(parent, edges[i].end);
if(n != m) /* 假如n與m不等,說明此邊沒有與現有的生成樹形成環路 */
{
parent[n] = m; /* 將此邊的結尾頂點放入下標爲起點的parent中。 表示此頂點已經在生成樹集合中*/
printf("(%d, %d) %d\n", edges[i].begin, edges[i].end, edges[i].weight);
}
}
}
算法源碼
參考資料
- 大話數據結構
- Kruskal算法(一)之 C語言詳解