數據結構與算法 -- 圖的應用之最小生成樹問題
前言
前面對 圖的存儲 和 **圖的遍歷(廣度優先/深度優先)**做了簡單的學習和了解,本篇文章,學習一下最小生成樹的問題,以及對應解決這個問題的兩種算法 普里姆算法 和 克魯斯卡爾算法
1.最小生成樹問題
首先看下面一道面試題:
假設目前有
N
個頂點,每個頂點連接的路徑不一樣,設計一個算法,快速查找出能覆蓋所有頂點的路徑。
其實這個問題並不是求解兩點之間的最短路徑,而是設計一個路線,能覆蓋所有的頂點。
如下連通圖:
那麼覆蓋所有頂點的路徑有一下幾種
-
方法一
V0 ——> V5 ——> V4 --> V3 --> V2 -->V1 -->V8 --> V6 -->V7
權重:11 + 26 + 20 + 22 + 18 + 21 + 24 + 19 = 161
-
方案二
V2 ——> V8 ——> V1 --> V0 --> V5 -->V6 -->V7 --> V3 -->V4
權重:8 + 12 + 10 + 11 + 17 + 19 + 16 + 7 = 100 -
方案三
權重:8 + 12 + 10 + 11 + 16 + 19 + 16 + 7 = 99
由此可以看出,方法三是最優的方案,這就是最小生成樹。
最小生成樹:把構成連通網的最小代價的生成樹稱之爲最小生成樹。即假設有N
個頂點,用N-1
條邊,連接所有頂點,而且權重的和最小的路徑。
2.最小生成樹求解(普里姆(Prim)算法)
普里姆算法思路:
1. 定義兩個數組,adjvew 用來保存相關頂點下標,lowcost 保存頂點之間的權值。
2. 初始化兩個數組,將與 V0 相關的 V1-V8 的所有頂點的權值賦值給 lowcost
adjvew[1-8],都賦值爲 0,表示都是與 V0 相關的頂點(後面循環修改)
3. 循環 lowcost 數組,根據權值,找到權值最新的頂點的下標 k
4. 更新 lowcost 數組
5. 循環所有頂點,找到與下標爲 k 的頂點,有關係的頂點,並更新 lowcost 數組和 adjvew 數組
注意:
更新 lowcost 數組的條件
1. 與下標爲 k 的頂點之間有連接
2. 當前下標爲 j 的頂點是否加入最小生成樹
3. 下標爲 k 的頂點與下標爲 j 的頂點的權值比較,小於,則更新,
簡單說就是要比較之前存儲的權值,小於,則更新。
接下來,我們詳細的解析一下上面的思路:
- 初始化
lowcost
和adjvew
lowcost
數組(將與 V0
相關的 V1-V8
的所有頂點的權值賦值給 lowcost
)
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
---|---|---|---|---|---|---|---|---|
0 | 10 | ∞ | ∞ | ∞ | 11 | ∞ | ∞ | ∞ |
默認將V0
加入到最小生成樹中,lowcost[0] = 0
,10
和 11
表示頂點V0 連接頂點V1
和V5
的權值。
adjvew
數組(都賦值爲 0,表示都是與 V0 相關的頂點)
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
---|---|---|---|---|---|---|---|---|
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
然後開始循環(從
i = 1
開始,默認第一個已經加入最小樹)
-
第一次循環
i = 1
由上面的表格看出,
10
是最小的權值,此時,
k = 1
,在lowcost
數組中10
最小。且滿足更新lowcost
數組的三個條件,所以,
lowcost[1] = 0
, 表示V1
已經加入最小生成樹,並打印信息lowcost
數組0 1 2 3 4 5 6 7 8 0 0 ∞ ∞ ∞ 11 ∞ ∞ ∞ 然後,循環所有頂點,找到下標爲
k
頂點各邊權值小於此前這些頂點未被加入生成樹權值,然後更新lowcost
數組和adjvew
數組V2 未加入最小生成樹,且權值 18 < ∞,lowcost【2】= 18,adjvew【2】= 1 V8 未加入最小生成樹,且權值 12 < ∞,lowcost【8】= 12,adjvew【8】= 1 V6 未加入最小生成樹,且權值 16 < ∞,lowcost【6】= 16,adjvew【6】= 1
lowcost
數組0 1 2 3 4 5 6 7 8 0 0 18 ∞ ∞ 11 16 ∞ 12 adjvew
數組(都賦值爲 0,表示都是與 V0 相關的頂點)0 1 2 3 4 5 6 7 8 0 0 1 0 0 0 1 0 1 18,16,12
對應V2、V6、V8
,是針對V1
相關的頂點,1
是因爲k = 1
,表示與V1
相連的頂點是V2、V6、V8
-
第二次循環
i = 2
從
lowcost
中找到 權值最小11
的j = 5
,便是k = 5
所以,
lowcost[5] = 0
,加入到最小生成樹中,並打印信息然後,循環所有頂點,找到下標爲
k = 5
頂點各邊權值小於此前這些頂點未被加入生成樹權值,然後更新lowcost
數組和adjvew
數組V6 未加入最小生成樹,且權值 17 > 16,不更新 V4 未加入最小生成樹,且權值 26 < ∞,lowcost【4】= 26,adjvew【4】= 5 V3 未加入最小生成樹,且權值 ∞ < ∞,不更新
此時,
lowcost
數組0 1 2 3 4 5 6 7 8 0 0 18 ∞ 26 0 16 ∞ 12 adjvew
數組(都賦值爲 0,表示都是與 V0 相關的頂點)0 1 2 3 4 5 6 7 8 0 0 1 0 5 0 1 0 1 1
,表示與V1
相連的頂點V2、V6、V8
,5
是因爲k = 5
,表示與V5
相連的頂點是V4
-
第三次循環
i = 3
從
lowcost
中找到 權值最小12
的j = 8
,便是k = 8
所以,
lowcost[8] = 0
,加入到最小生成樹中,並打印信息然後,循環所有頂點,找到下標爲
k = 8
頂點各邊權值小於此前這些頂點未被加入生成樹權值,然後更新lowcost
數組和adjvew
數組V2 未加入最小生成樹,且權值 8 < 18,lowcost【2】= 8,adjvew【2】= 8 V3 未加入最小生成樹,且權值 21 < ∞,lowcost【3】= 21,adjvew【3】= 8
此時,
lowcost
數組0 1 2 3 4 5 6 7 8 0 0 8 21 26 0 16 ∞ 0 V0、V1、V5、V8
都已經加入最小生成樹adjvew
數組(都賦值爲 0,表示都是與 V0 相關的頂點)0 1 2 3 4 5 6 7 8 0 0 8 8 5 0 1 0 1 1
,表示與V1
相連的頂點V6、V8
,5
,表示與V5
相連的頂點是V4
8
是因爲k = 8
,表示與V8
相連的頂點是V2、V3
-
第四次循環
i = 4
從
lowcost
中找到 權值最小8
的j = 2
,便是k = 2
所以,
lowcost[2] = 0
,加入到最小生成樹中,並打印信息然後,循環所有頂點,找到下標爲
k = 2
頂點各邊權值小於此前這些頂點未被加入生成樹權值,然後更新lowcost
數組和adjvew
數組V3 未加入最小生成樹,且權值 22 > 21,不更新
此時,
lowcost
數組0 1 2 3 4 5 6 7 8 0 0 0 21 26 0 16 ∞ 0 V0、V1、V5、V8
都已經加入最小生成樹adjvew
數組(都賦值爲 0,表示都是與 V0 相關的頂點)0 1 2 3 4 5 6 7 8 0 0 8 8 5 0 1 0 1 1
,表示與V1
相連的頂點V6、V8
,
5
,表示與V5
相連的頂點是V4
8
是因爲k = 8
,表示與V8
相連的頂點是V2、V3
-
第五次循環
i = 5
從
lowcost
中找到 權值最小16
的j = 6
,便是k = 6
所以,
lowcost[6] = 0
,加入到最小生成樹中,並打印信息然後,循環所有頂點,找到下標爲
k = 6
頂點各邊權值小於此前這些頂點未被加入生成樹權值,然後更新lowcost
數組和adjvew
數組V7 未加入最小生成樹,且權值 19 < ∞,lowcost【7】= 19,adjvew【7】= 6 V3 未加入最小生成樹,且權值 22 > 21,不更新
此時,
lowcost
數組0 1 2 3 4 5 6 7 8 0 0 0 21 26 0 0 19 0 V0、V1、V2、V5、V6、V8
都已經加入最小生成樹adjvew
數組(都賦值爲 0,表示都是與 V0 相關的頂點)0 1 2 3 4 5 6 7 8 0 0 8 8 5 0 1 6 1 1
,表示與V1
相連的頂點V6、V8
,5
,表示與V5
相連的頂點是V4
8
,表示與V8
相連的頂點是V2、V3
6
是因爲k = 6
,表示與V6
相連的頂點是V7
-
第六次循環
i = 6
從
lowcost
中找到 權值最小19
的j = 7
,便是k = 7
所以,
lowcost[7] = 0
,加入到最小生成樹中,並打印信息然後,循環所有頂點,找到下標爲
k = 7
頂點各邊權值小於此前這些頂點未被加入生成樹權值,然後更新lowcost
數組和adjvew
數組V4 未加入最小生成樹,且權值 7 < 26,lowcost【4】= 7,adjvew【4】= 7 V3 未加入最小生成樹,且權值 16 < 21,lowcost【3】= 16,adjvew【3】= 7
此時,
lowcost
數組0 1 2 3 4 5 6 7 8 0 0 0 16 7 0 0 0 0 V0、V1、V2、V5、V6、V7、V8
都已經加入最小生成樹adjvew
數組(都賦值爲 0,表示都是與 V0 相關的頂點)0 1 2 3 4 5 6 7 8 0 0 8 7 7 0 1 6 1 1
,表示與V1
相連的頂點V6、V8
,8
,表示與V8
相連的頂點是V2
6
,表示與V6
相連的頂點是V7
7
是因爲k = 7
,表示與V7
相連的頂點是V3,V4
-
第七次循環
i = 7
從lowcost
中找到 權值最小7
的j = 4
,便是k = 4
所以,
lowcost[4] = 0
,加入到最小生成樹中,並打印信息然後,循環所有頂點,找到下標爲
k = 4
頂點各邊權值小於此前這些頂點未被加入生成樹權值,然後更新lowcost
數組和adjvew
數組V3 未加入最小生成樹,且權值 20 > 16,不更新
此時,
lowcost
數組0 1 2 3 4 5 6 7 8 0 0 0 16 0 0 0 0 0 V0、V1、V2、V4、V5、V6、V7、V8
都已經加入最小生成樹adjvew
數組(都賦值爲 0,表示都是與 V0 相關的頂點)0 1 2 3 4 5 6 7 8 0 0 8 7 7 0 1 6 1 1
,表示與V1
相連的頂點V6、V8
,8
,表示與V8
相連的頂點是V2
6
,表示與V6
相連的頂點是V7
7
是因爲k = 7
,表示與V7
相連的頂點是V3,V4
-
第八次循環
i = 8
從
lowcost
中找到 權值最小16
的j = 3
,便是k = 3
所以,
lowcost[3] = 0
,加入到最小生成樹中,並打印信息然後,循環所有頂點,找到下標爲
k = 3
頂點各邊權值小於此前這些頂點未被加入生成樹權值,然後更新lowcost
數組和adjvew
數組V3 未加入最小生成樹,且權值 20 > 16,不更新
此時,
lowcost
數組0 1 2 3 4 5 6 7 8 0 0 0 0 0 0 0 0 0 V0、V1、V2、V3、V4、V5、V6、V7、V8
都已經加入最小生成樹adjvew
數組(都賦值爲 0,表示都是與 V0 相關的頂點)0 1 2 3 4 5 6 7 8 0 0 8 7 7 0 1 6 1 1
,表示與V1
相連的頂點V6、V8
,8
,表示與V8
相連的頂點是V2
6
,表示與V6
相連的頂點是V7
7
是因爲k = 7
,表示與V7
相連的頂點是V3,V4
代碼實現:
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
#define MAXEDGE 20
#define MAXVEX 20
typedef int Status;
typedef struct {
int arc[MAXVEX][MAXVEX];
int numVertexes;
int numEdges;
}MGraph;
/* Prim算法生成最小生成樹 */
void MiniSpanTree_Prim(MGraph G)
{
int min, i, j, k = 0;
int sum = 0;
int adjvex[MAXVEX]; // 保存相關頂點下標
int lowcost[MAXVEX]; // 保存相關頂點的權重
// 初始化第一個權值爲0 ,將V0加入生成最小樹
lowcost[0] = 0;
// 初始化第一個頂點的下標爲0
adjvex[0] = 0;
// 初始化
for (i = 1; i < G.numVertexes; i++) {
// 將v0頂點與之有邊的權值存入數組
lowcost[i] = G.arc[0][i];
// 初始化默認都爲v0的下標
adjvex[i] = 0;
}
// 循環除了下標爲 0 以外的全部頂點,找到z權重最小且沒有加入到s最小樹中的頂點
for (i = 1; i < G.numVertexes; i++) {
min = INFINITY;
j = 1; k = 0;
while (j < G.numVertexes) {
if (lowcost[j] != 0 && lowcost[j] < min) {
// 讓當前權值成爲最小值,更新min
min = lowcost[j];
// 當前最小值的下標賦值給 k
k = j;
}
j++;
}
}
// 打印
printf("(V%d, V%d) = %d\n", adjvex[k], k, G.arc[adjvex[k]][k]);
sum += G.arc[adjvex[k]][k];
// 當前頂點的權值設置爲0 ,標誌爲已經加入最小樹
lowcost[k] = 0;
// 循環所有頂點,找到與頂點k 相連接的頂點
for (j = 1; j < G.numVertexes; j++) {
if (lowcost[j] != 0 && G.arc[k][j] < lowcost[j]) {
// 將較小的權值存入lowcost相應位置
lowcost[j] = G.arc[k][j];
// 下標爲k的頂點存入adjvex
adjvex[j] = k;
}
}
printf("sum = %d\n", sum);
}
2.最小生成樹求解(克魯斯卡爾算法)
克魯斯卡爾算法思路:
1.將 鄰接矩陣 轉換爲 邊表數組()
2.對邊表數組根據權重值按照從小到大的順序排序
3.遍歷所有的邊,打印不閉環的邊,通過 parent 數組找到邊的連接信息,避免閉環
4.如果不存在閉環問題,則加入到最小生成樹中,並修改 parent 數組
克魯斯卡爾算法詳細解析如下:
設計如下的邊表數據結構:
/* 對邊集數組Edge結構的定義 */
typedef struct
{
int begin; // 開始
int end; // 結束
int weight; // 權重
}Edge ;
那麼對上面所說的圖的邊表,排序後如下:
begin | end | weight |
---|---|---|
4 | 7 | 7 |
2 | 8 | 8 |
0 | 1 | 10 |
0 | 5 | 11 |
1 | 8 | 12 |
3 | 7 | 16 |
1 | 6 | 16 |
5 | 6 | 17 |
1 | 2 | 18 |
6 | 7 | 19 |
3 | 4 | 20 |
3 | 8 | 21 |
2 | 3 | 22 |
3 | 6 | 24 |
4 | 5 | 26 |
初始化parent
數組,給初始值爲0,默頂點間認沒有連接
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
然後開始遍歷 排好序的邊表:
定義n
和 m
,分別表示begin
和 end
,如果 m = n
,表示 begin
和 end
連接,就會產生閉合的環.
-
第
0
次,i = 0
begin = 4,end = 7
,n = 4
,m = 7
n != m
,所以parent[4] = 7
,然後打印計算權重
parent
數組如下:
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0 | 0 | 0 | 7 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
-
第
1
次,i = 1
begin = 2,end = 8
,n = 2
,m = 8
n != m
,所以parent[2] = 8
,然後打印計算權重
parent
數組如下:
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0 | 8 | 0 | 7 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
-
第
2
次,i = 2
begin = 0,end = 1
,n = 0
,m = 1
n != m
,所以parent[0] = 1
,然後打印計算權重
parent
數組如下:
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
1 | 0 | 8 | 0 | 7 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
-
第
3
次,i = 3
begin = 0,end = 5
,n = 0
,m = 5
然後從
parent 數組
中,找到當前頂點的尾部下標( 幫助我們判斷2點之間是否存在閉環問題)
parent[0] = 1
,所以n = 1
,parent[5] = 0
,直接返回5
n != m
,所以parent[1] = 5
,然後打印計算權重parent
數組如下:
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
1 | 5 | 8 | 0 | 7 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
-
第
4
次,i = 4
begin = 1,end = 8
,n = 1
,m = 8
然後從
parent 數組
中,可以找到當前頂點的尾部下標( 幫助我們判斷2點之間是否存在閉環問題)
parent[1] = 5
,所以n = 5
,parent[8] = 0
,直接返回8
n != m
,所以parent[5] = 8
,然後打印計算權重
parent
數組如下:
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
1 | 5 | 8 | 0 | 7 | 8 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
-
第
5
次,i = 5
begin = 3,end = 7
,n = 3
,m = 7
然後從
parent 數組
中,找到當前頂點的尾部下標( 幫助我們判斷2點之間是否存在閉環問題)
parent[3] = 0
,所以n = 3
,parent[7] = 0
,直接返回7
n != m
,所以parent[3] = 7
,然後打印計算權重parent
數組如下:
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
1 | 5 | 8 | 7 | 7 | 8 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
-
第
6
次,i = 6
begin = 1,end = 6
,n = 1
,m = 6
然後從
parent 數組
中,找到當前頂點的尾部下標( 幫助我們判斷2點之間是否存在閉環問題)
parent[1] = 5,parent[5] = 8
,所以n = 8
,parent[6] = 0
,直接返回6
n != m
,所以parent[8] = 6
,然後打印計算權重parent
數組如下:
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
1 | 5 | 8 | 7 | 7 | 8 | 0 | 0 | 6 | 0 | 0 | 0 | 0 | 0 | 0 |
-
第
7
次,i = 7
begin = 5,end = 6
,n = 5
,m = 6
然後從
parent 數組
中,找到當前頂點的尾部下標( 幫助我們判斷2點之間是否存在閉環問題)
parent[5] = 8,parent[8] = 6
,所以n = 6
,parent[6] = 0
,直接返回6
n = m
,所以不更新parent數組
,所以不能加入最小樹parent
數組如下:
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
1 | 5 | 8 | 7 | 7 | 8 | 0 | 0 | 6 | 0 | 0 | 0 | 0 | 0 | 0 |
-
第
8
次,i = 8
begin = 1,end = 2
,n = 1
,m = 2
然後從
parent 數組
中,找到當前頂點的尾部下標( 幫助我們判斷2點之間是否存在閉環問題)
parent[1] = 5,parent[5] = 8,parent[8] = 6
,所以n = 6
,parent[2] = 8,parent[8] = 6
,直接返回m = 6
n = m
,所以不更新parent數組
,所以不能加入最小樹parent
數組如下:
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
1 | 5 | 8 | 7 | 7 | 8 | 0 | 0 | 6 | 0 | 0 | 0 | 0 | 0 | 0 |
-
第
9
次,i = 9
begin = 6,end = 7
,n = 6
,m = 7
然後從
parent 數組
中,找到當前頂點的尾部下標( 幫助我們判斷2點之間是否存在閉環問題)
parent[6] = 0
,所以n = 6
,parent[7] = 9
,直接返回m = 6
n != m
,所以更新parent數組
,所以parent[6] = 7
,然後打印計算權重parent
數組如下:
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
1 | 5 | 8 | 7 | 7 | 8 | 7 | 0 | 6 | 0 | 0 | 0 | 0 | 0 | 0 |
-
第
10
次,i = 10
begin = 3,end = 4
,n = 3
,m = 4
然後從
parent 數組
中,找到當前頂點的尾部下標( 幫助我們判斷2點之間是否存在閉環問題)
parent[3] = 7,parent[7] = 0
,所以n = 7
,parent[4] = 7,parent[7] = 0
,直接返回m = 7
n = m
,所以不更新parent數組
,所以不能加入最小樹parent
數組如下:
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
1 | 5 | 8 | 7 | 7 | 8 | 7 | 0 | 6 | 0 | 0 | 0 | 0 | 0 | 0 |
-
第
11
次,i = 11
begin = 3,end = 8
,n = 3
,m = 8
然後從
parent 數組
中,找到當前頂點的尾部下標( 幫助我們判斷2點之間是否存在閉環問題)
parent[3] = 7,parent[7] = 0
,所以n = 7
,parent[8] = 6,parent[6] = 7
,直接返回m = 7
n = m
,所以不更新parent數組
,所以不能加入最小樹parent
數組如下:
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
1 | 5 | 8 | 7 | 7 | 8 | 7 | 0 | 6 | 0 | 0 | 0 | 0 | 0 | 0 |
-
第
12
次,i = 12
begin = 2,end = 3
,n = 2
,m = 3
然後從
parent 數組
中,找到當前頂點的尾部下標( 幫助我們判斷2點之間是否存在閉環問題)
parent[2] = 8,parent[8] = 6,parent[6] = 7
,所以n = 7
,parent[3] = 7,parent[7] = 0
,直接返回m = 7
n = m
,所以不更新parent數組
,所以不能加入最小樹parent
數組如下:
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
1 | 5 | 8 | 7 | 7 | 8 | 7 | 0 | 6 | 0 | 0 | 0 | 0 | 0 | 0 |
-
第
13
次,i = 13
同上分析,
m = n
,不更新 -
第
14
次,i = 14
同上分析,m = n
,不更新parent
數組如下:
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
1 | 5 | 8 | 7 | 7 | 8 | 7 | 0 | 6 | 0 | 0 | 0 | 0 | 0 | 0 |
最終遍歷完所有的邊,找到最小樹
代碼實現:
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
#define MAXEDGE 20
#define MAXVEX 20
#define INFINITYC 65535
typedef int Status;
// 圖結構
typedef struct
{
int arc[MAXVEX][MAXVEX];
int numVertexes, numEdges;
}MGraph;
// 邊表結構
typedef struct {
int begin;
int end;
int weight;
}Edge;
/* 交換權值以及頭和尾 */
void Swapn(Edge *edges,int i, int j)
{
int tempValue;
//交換edges[i].begin 和 edges[j].begin 的值
tempValue = edges[i].begin;
edges[i].begin = edges[j].begin;
edges[j].begin = tempValue;
//交換edges[i].end 和 edges[j].end 的值
tempValue = edges[i].end;
edges[i].end = edges[j].end;
edges[j].end = tempValue;
//交換edges[i].weight 和 edges[j].weight 的值
tempValue = edges[i].weight;
edges[i].weight = edges[j].weight;
edges[j].weight = tempValue;
}
/* 對權值進行排序 */
void sort(Edge edges[],MGraph *G)
{
//對權值進行排序(從小到大)
int i, j;
for ( i = 0; i < G->numEdges; i++)
{
for ( j = i + 1; j < G->numEdges; j++)
{
if (edges[i].weight > edges[j].weight)
{
Swapn(edges, i, j);
}
}
}
printf("邊集數組根據權值排序之後的爲:\n");
for (i = 0; i < G->numEdges; i++)
{
printf("(%d, %d) %d\n", edges[i].begin, edges[i].end, edges[i].weight);
}
}
int Find(int *parent, int f)
{
while ( parent[f] > 0)
{
f = parent[f];
}
return f;
}
/* 生成最小生成樹 */
void MiniSpanTree_Kruskal(MGraph G)
{
int i, j, n, m;
int sum = 0;
int k = 0;
int parent[MAXVEX];
Edge edges[MAXEDGE];
// 構建邊表
for (i = 0; i < G.numVertexes - 1; i++) {
for (j = 1 + i; j < G.numVertexes; j++) {
if (G.arc[i][j] < INFINITYC) {
edges[k].begin = i;
edges[k].end = j;
edges[k].weight = G.arc[i][j];
k++;
}
}
}
// 排序
sort(edges, &G);
// 初始化parent 數組爲0. 9個頂點;
for (i = 0; i < MAXVEX; i++)
parent[i] = 0;
// 循環邊表
for (i = 0; i < G.numEdges; i++) {
//獲取begin,end 在parent 數組中的信息;
//如果n = m ,將begin 和 end 連接,就會產生閉合的環.
n = Find(parent,edges[i].begin);
m = Find(parent,edges[i].end);
// n與m不等,說明此邊沒有與現有的生成樹形成環路
if (n != m) {
parent[n] = m;
printf("(%d, %d) %d\n", edges[i].begin, edges[i].end, edges[i].weight);
sum += edges[i].weight;
}
}
printf("sum = %d\n",sum);
}