這是我說的那個博客,爲了防止做者刪除,我直接複製過來,網頁是最小生成樹html
邊賦以權值的圖稱爲網或帶權圖,帶權圖的生成樹也是帶權的,生成樹T各邊的權值總和稱爲該樹的權。算法
最小生成樹(MST):權值最小的生成樹。數組
生成樹和最小生成樹的應用:要連通n個城市須要n-1條邊線路。能夠把邊上的權值解釋爲線路的造價。則最小生成樹表示使其造價最小的生成樹。數據結構
構造網的最小生成樹必須解決下面兩個問題:測試
一、儘量選取權值小的邊,但不能構成迴路;ui
二、選取n-1條恰當的邊以連通n個頂點;spa
MST性質:假設G=(V,E)是一個連通網,U是頂點V的一個非空子集。若(u,v)是一條具備最小權值的邊,其中u∈U,v∈V-U,則必存在一棵包含邊(u,v)的最小生成樹。 .net
1.prim算法設計
基本思想:假設G=(V,E)是連通的,TE是G上最小生成樹中邊的集合。算法從U={u0}(u0∈V)、TE={}開始。重複執行下列操做:unix
在全部u∈U,v∈V-U的邊(u,v)∈E中找一條權值最小的邊(u0,v0)併入集合TE中,同時v0併入U,直到V=U爲止。
此時,TE中必有n-1條邊,T=(V,TE)爲G的最小生成樹。
Prim算法的核心:始終保持TE中的邊集構成一棵生成樹。
注意:prim算法適合稠密圖,其時間複雜度爲O(n^2),其時間複雜度與邊得數目無關,而kruskal算法的時間複雜度爲O(eloge)跟邊的數目有關,適合稀疏圖。
看了上面一大段文字是否是感受有點暈啊,爲了更好理解我在這裏舉一個例子,示例以下:
(1)圖中有6個頂點v1-v6,每條邊的邊權值都在圖上;在進行prim算法時,我先隨意選擇一個頂點做爲起始點,固然咱們通常選擇v1做爲起始點,好,如今咱們設U集合爲當前所找到最小生成樹裏面的頂點,TE集合爲所找到的邊,如今狀態以下:
U={v1}; TE={};
(2)如今查找一個頂點在U集合中,另外一個頂點在V-U集合中的最小權值,以下圖,在紅線相交的線上找最小值。
經過圖中咱們能夠看到邊v1-v3的權值最小爲1,那麼將v3加入到U集合,(v1,v3)加入到TE,狀態以下:
U={v1,v3}; TE={(v1,v3)};
(3)繼續尋找,如今狀態爲U={v1,v3}; TE={(v1,v3)};在與紅線相交的邊上查找最小值。
咱們能夠找到最小的權值爲(v3,v6)=4,那麼咱們將v6加入到U集合,並將最小邊加入到TE集合,那麼加入後狀態以下:
U={v1,v3,v6}; TE={(v1,v3),(v3,v6)}; 如此循環一下直到找到全部頂點爲止。
(4)下圖像咱們展現了所有的查找過程:
2.prim算法程序設計
(1)因爲最小生成樹包含每一個頂點,那麼頂點的選中與否就能夠直接用一個數組來標記used[max_vertexes];(咱們這裏直接使用程序代碼中的變量定義,這樣也易於理解);當選中一個數組的時候那麼就標記,如今就有一個問題,怎麼來選擇最小權值邊,注意這裏最小權值邊是有限制的,邊的一個頂點必定在已選頂點中,另外一個頂點固然就是在未選頂點集合中了。我最初的一個想法就是窮搜了,就是在一個集合中選擇一個頂點,來查找到另外一個集合中的最小值,這樣雖然很易於理解,可是很明顯效率不是很高,在嚴蔚敏的《數據結構》上提供了一種比較好的方法來解決:設置兩個輔助數組lowcost[max_vertexes]和closeset[max_vertexes],lowcost[max_vertexes]數組記錄從U到V-U具備最小代價的邊。對於每一個頂點v∈V-U,closedge[v], closeset[max_vertexes]記錄了該邊依附的在U中的頂點。
注意:咱們在考慮兩個頂點無關聯的時候設爲一個infinity 1000000最大值。
說了這麼多,感受有點羅嗦,仍是發揚原來的風格舉一個例子來講明,示例以下:
過程以下表:頂點標號都比圖中的小1,好比v1爲0,v2爲1,這裏首先選擇v1點。
Lowcost[0] |
Lowcost[1] |
Lowcost[2] |
Lowcost[3] |
Lowcost[4] |
Lowcost[5] |
U |
V-U |
|
closeset |
v1,infinity |
v1,6 |
v1,1 |
v1,5 |
v1,infinity |
v1,infinity |
v1 |
v1,v2,v3,v4,v5,v6 |
從這個表格能夠看到依附到v1頂點的v3的Lowcost最小爲1,那麼選擇v3,選擇了以後咱們必需要更新Lowcost數組的值,由於記錄從U到V-U具備最小代價的邊,加入以後就會改變。這裏更新Lowcost和更新closeset數組可能有點難理解,
for (k=1;k<vcount;k++)
if (!used[k]&&(G[j][k]<lowcost[k]))
{ lowcost[k]=G[j][k];
closeset[k]=j; }
}
j爲咱們已經選出來的頂點,若是G[j][k]<lowcost[k],則意味着最小權值邊發生變化,更新該頂點的最小lowcost權值,依附的頂點確定就是剛剛選出的頂點j,closeset[k]=j。
Lowcost[0] |
Lowcost[1] |
Lowcost[2] |
Lowcost[3] |
Lowcost[4] |
Lowcost[5] |
U |
V-U |
|
closeset |
v1,infinity |
v1,6 |
v1,1 |
v1,5 |
v3,6 |
v3,4 |
v1,v3 |
v1,v2,v4,v5,v6 |
這樣一直選擇下去直到選出全部的頂點。
(2)上面把查找最小權值的邊結束了,可是這裏有一個問題,就是咱們沒有存儲找到的邊,若是要求你輸出找到的邊那麼這個程序就須要改進了,咱們剛開始的時候選取的是v1做爲第一個選擇的頂點,那咱們設置一個father[]數組來記錄每一個節點的父節點,固然v1的父節點確定沒有,那麼咱們設置一個結束標誌爲-1,每次找到一個新的節點就將它的父節點設置爲他依附的節點,這樣就能夠準確的記錄邊得存儲了。
語法:prim(Graph G,int vcount,int father[]); |
|
參數: |
|
G: |
圖,用鄰接矩陣表示 |
vcount: |
表示圖的頂點個數 |
father[]: |
用來記錄每一個節點的父節點 |
返回值: |
null |
注意: |
|
|
常數max_vertexes爲圖最大節點數 |
|
常數infinity爲無窮大 |
數組存儲從0開始 |
|
若是下面的源程序有錯請參照測試程序。 |
|
源程序: |
|
|
#define infinity 1000000 int closeset[max_vertexes],used[max_vertexes]; int min; /* 最短距離初始化爲其餘節點到1號節點的距離 */ /* 標記全部節點的依附點皆爲默認的1號節點 */
/* vcount個節點至少須要vcount-1條邊構成最小生成樹 */ min = infinity; /* 找知足條件的最小權值邊的節點k */ /* 邊權值較小且不在生成樹中 */ { min = lowcost[k]; j=k; } /* 發現更小的權值 */ lowcost[k]=G[j][k];/*更新最小權值*/ } |
測試程序:
測試用例:
1 2 6
1 3 1
1 4 5
2 3 5
2 5 3
3 4 5
3 5 6
3 6 4
5 6 6
4 6 2
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define infinity 1000000
#define max_vertexes 6
typedef int Graph[max_vertexes][max_vertexes];
void prim(Graph G,int vcount,int father[])
{
int i,j,k;
int lowcost[max_vertexes];
int closeset[max_vertexes],used[max_vertexes];
int min;
for (i=0;i<vcount;i++)
{
/* 最短距離初始化爲其餘節點到1號節點的距離 */
lowcost[i]=G[0][i];
/* 標記全部節點的依附點皆爲默認的1號節點 */
closeset[i]=0;
used[i]=0;
father[i]=-1;
}
used[0]=1; /*第一個節點是在s集合裏的*/
/* vcount個節點至少須要vcount-1條邊構成最小生成樹 */
for (i=1;i<=vcount-1;i++)
{
j=0;
min = infinity;
/* 找知足條件的最小權值邊的節點k */
for (k=1;k<vcount;k++)
/* 邊權值較小且不在生成樹中 */
if ((!used[k])&&(lowcost[k]<min))
{
min = lowcost[k];
j=k;
}
father[j]=closeset[j];
printf("%d %d\n",j+1,closeset[j]+1);//打印邊
used[j]=1;;//把第j個頂點併入了U中
for (k=1;k<vcount;k++)
/* 發現更小的權值 */
if (!used[k]&&(G[j][k]<lowcost[k]))
{
lowcost[k]=G[j][k];/*更新最小權值*/
closeset[k]=j;;/*記錄新的依附點*/
}
}
}
int main()
{
FILE *fr;
int i,j,weight;
Graph G;
int fatheer[max_vertexes];
for(i=0; i<max_vertexes; i++)
for(j=0; j<max_vertexes; j++)
G[i][j] = infinity;
fr = fopen("prim.txt","r");
if(!fr)
{
printf("fopen failed\n");
exit(1);
}
while(fscanf(fr,"%d%d%d", &i, &j, &weight) != EOF)
{
G[i-1][j-1] = weight;
G[j-1][i-1] = weight;
}
prim(G,max_vertexes,fatheer);
return 0;
}
程序結果:
3 1
6 3
4 6
2 3
5 2
請按任意鍵繼續. . .