標籤:針對 儲存 ora [1] 尋找 cal tab 儲存結構 並且
我們在圖的定義中說過,帶有權值的圖就是網結構。一個連通圖的產生樹是一個極小的連通子圖,它含有圖中全部的頂點,但只有足以構成一棵樹的n-1條邊。所謂的最小成本,就是n個頂點,用n-1條邊把一個連通圖串連起來,並且使得權值的和最小。綜合以上兩個概念,我們可以得出:構造連通網的最小代價產生樹,即最小產生樹(Minimum Cost Spanning Tree)。
找連通圖的最小產生樹,經典的有兩種演算法,普裡姆演算法和克魯斯卡爾演算法,這裡介紹普裡姆演算法。
為了能夠講明白這個演算法,我們先構造網圖的鄰接矩陣,7-6-3的右圖所示。
也就是說,現在我們已經有了一個儲存結構為MGraph的MG(見《鄰接矩陣建立圖》)。MG有9個頂點,它的二維數組如右圖所示,數組中我們使用65535代表無窮。
下面我們對著程式和每一步迴圈的圖示來看:
演算法代碼:(改編自《大話資料結構》)
C++ Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
|
|
/* Prim演算法產生最小產生樹 */ void MiniSpanTree_Prim(MGraph MG)
{
int min, i, j, k;
int adjvex[MAXVEX];/* 儲存相關頂點下標 */
int lowcost[MAXVEX];/* 儲存相關頂點間邊的權值 */
lowcost[0] = 0;/* 初始化第一個權值為0,即v0加入產生樹 */
/* lowcost的值為0,在這裡就是此下標的頂點已經加入產生樹 */
adjvex[0] = 0;/* 初始化第一個頂點下標為0 */
cout << "最小產生樹的邊為:" << endl;
for (i = 1; i < MG.numVertexes; i++)
{
lowcost[i] = MG.arc[0][i];/* 將v0頂點與之有邊的權值存入數組 */
adjvex[i] = 0;/* 初始化都為v0的下標 */
}
for (i = 1; i < MG.numVertexes; i++)
{
min = INFINITY; /* 初始化最小權值為∞, */
j = 1;
k = 0;
while (j < MG.numVertexes)/* 迴圈全部頂點 */
{
if (lowcost[j] != 0 && lowcost[j] < min)/* 如果權值不為0且權值小於min */
{
min = lowcost[j];/* 則讓當前權值成為最小值 */
k = j;/* 將當前最小值的下標存入k */
}
j++;
}
cout << "(" << adjvex[k] << ", " << k << ")" << " "; /* 列印當前頂點邊中權值最小的邊 */
lowcost[k] = 0;/* 將當前頂點的權值設定為0,表示此頂點已經完成任務 */
for (j = 1; j < MG.numVertexes; j++)/* 迴圈所有頂點 */
{
/* 如果下標為k頂點各邊權值小於此前這些頂點未被加入產生樹權值 */
if (lowcost[j] != 0 && MG.arc[k][j] < lowcost[j])
{
lowcost[j] = MG.arc[k][j];/* 將較小的權值存入lowcost相應位置 */
adjvex[j] = k;/* 將下標為k的頂點存入adjvex */
}
}
}
cout << endl;
} |
1、程式中1~16行是初始化操作,其中第7~8行 adjvex[0] = 0 意思是現在從頂點v0開始(事實上從那一點開始都無所謂,假定從v0開始),lowcost[0]= 0 表示v0已經被納入到最小產生樹中,之後凡是lowcost數組中的值被設為0就表示此下標的頂點被納入最小產生樹。
2、第11~15行表示讀取鄰接矩陣的第一行資料,所以 lowcost數組為{ 0 ,10, 65535, 65535, 65535, 11, 65535, 65535, 65535 },而adjvex數組為全0。至此初始化完畢。
3、第17~49行共迴圈了8次,i從1一直累加到8,整個迴圈過程就是構造最小產生樹的過程。
4、第24~33行,經過迴圈後min = 10, k = 1。注意26行的if 判斷lowcost[j] != 0 表示已經是產生樹的頂點則不參加最小權值的尋找。
5、第35行,因k = 1, adjvex[1] = 0, 所以列印結果為(0, 1),表示v0 至 v1邊為最小產生樹的第一條邊,如的第一個小圖。
6、第36行,因k = 1 將lowcost[k] = 0 就是說頂點v1納入到最小產生樹中,此時lowcost數組為{ 0,0, 65535, 65535, 65535, 11, 65535, 65535, 65535 }
7、第38~47行,j 迴圈從1 到8, 因k = 1,尋找鄰接矩陣的第v1行的各個權值,與lowcost數組對應值比較,若更小則修改lowcost值,並將k值存入adjvex數組中。所以最終lowcost = { 0,0, 18, 65535, 65535, 11, 16, 65535, 12 }。 adjvex數組的值為 {0, 0, 1, 0, 0, 0, 1, 0, 1 }。這裡的if判斷也表示v0和v1已經是產生樹的頂點不參與最小權值的比對了。
上面所述為第一次迴圈,對應i = 1的第一個小圖,由於要用文字描述清楚整個流程比較繁瑣,下面給出i為不同值一次迴圈下來後的產生樹圖示,所謂一圖值千言,大家對著圖示自己類比地迴圈8次就能理解普裡姆演算法的思想了。
即最小產生樹的邊為:(0, 1), (0, 5), (1, 8), (8, 2), (1, 6), (6, 7), (7, 4), (7, 3)
最後再來總結一下普裡姆演算法的定義:
假設N = (V{E} )是連通網,TE是N上最小產生樹的集合。演算法從U = { u0} ( uo V),TE = { } 開始。重複執行下述操作:在所有
uU,v V - U 的邊(u, v) E 中找一條代價最小的邊(u0 , v0) 併入集合TE, 同時v0 併入U, 直至 U = V 為止。此時TE 中必有n-1 條邊, 則 T = (V,{TE} ) 為N的最小產生樹。
由演算法代碼中的迴圈嵌套可得知此演算法的時間複雜度為O(n^2)。
對比普裡姆和克魯斯卡爾演算法,克魯斯卡爾演算法主要針對邊來展開,邊數少時效率比較高,所以對於稀疏圖有較大的優勢;而普裡姆演算法對於稠密圖,即邊數非常多的情況下更好一些。
普裡姆Prim演算法 - 圖解最小產生樹