1. 程式人生 > >Prim演算法求圖的最小生成樹

Prim演算法求圖的最小生成樹

演算法之-----使用prim演算法求得最小生成樹的權值

程式碼實現:

1.輸入:n,m(n代表無向圖頂點數,m代表邊數)

   輸入:m行(每行輸入內容為(i,j,c)分別代表每條邊的起點、終點和權值)

2. 輸出:最小生成樹的權值

/*---prim演算法時間複雜度O(n^2),n為頂點數,
時間複雜度與邊的數目無關,因此適用於求邊稠密的圖的最小生成樹---*/
#include<stdio.h>
#include<stdlib.h>
#include<limits.h>
#include<stdbool.h>

#define MAX_VERTEX_NUM 20  //圖中頂點最大個數 
#define INFINITY INT_MAX  //無窮大∞ 

typedef char VertexType;   //頂點名稱型別
typedef int VRType; //邊的權值型別

//使用鄰接矩陣作為圖的儲存結構
typedef struct
{
    VertexType vexs[MAX_VERTEX_NUM];  //頂點向量
    VRType arcs[MAX_VERTEX_NUM][MAX_VERTEX_NUM];  //鄰接矩陣,其值為相鄰頂點的邊的權值
    int vexnum,arcnum;   //圖的頂點數和邊數 
 }MGraph;

 //建立圖
 //獲取頂點在頂點向量中的下標,不存在返回-1 
 int locateVertex(MGraph G, VertexType v)
 {
    int i;
    for(i=0;i<G.vexnum;i++)
        if(G.vexs[i]==v)
            return i;
    return -1;
 }

 void createMGraph(MGraph *mg)
 {
   int i,j; 
	int M[20]={'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t'};
    //輸入圖的頂點數和邊數
    scanf("%d %d",&(mg->vexnum),&(mg->arcnum));
    getchar();
   
    //輸入圖的頂點名稱
    for(i=0;i<mg->vexnum;i++)
    {
       mg->vexs[i]=M[i];
     }
     //初始化鄰接矩陣
     for(i=0;i<mg->vexnum;i++)
        for(j=0;j<mg->vexnum;j++)
         mg->arcs[i][j] = INFINITY;

    //輸入邊的兩個頂點個權值v1 v2 w
    char v1,v2;
    int w,k,m;
    for(k=0;k<mg->arcnum;k++)
    {   
        //輸入第%d條邊兩個頂點和權值
        scanf("%c %c %d",&v1,&v2,&w);
        getchar();
        i = locateVertex(*mg,v1);
        j = locateVertex(*mg,v2);
        mg->arcs[i][j] = mg->arcs[j][i] = w;    //無向圖
    //  mg->arcs[i][j] = w;           //有向圖     
    }
  } 

//prim演算法構造圖的最小生成樹 
//記錄從頂點集U到V-U的代價最小的邊的輔助陣列定義
typedef struct
{
    VertexType adjvex;   //頂點名稱
    VRType lowcost;    //對應邊的權值
}closedge[MAX_VERTEX_NUM];

 void PRIM(MGraph G, VertexType u)
 {
    int k,i,j,sumw=0;
    closedge closedge;
    k = locateVertex(G,u);   //獲取起始頂點的下標
    //初始化輔助陣列
    for(i=0;i<G.vexnum;i++)
    {
        if(i!=k)
        {
            closedge[i].lowcost = G.arcs[k][i];
            closedge[i].adjvex = u;
        }
    }
    closedge[k].lowcost = 0;  //初始,U={u} 
    //最小生成樹邊及權值
    for(i=1;i<G.vexnum;i++)           //選擇其餘G.vexnum-1個頂點 
    {
        int min = INFINITY,v,index = k;
        for(v=0;v<G.vexnum;v++)          //求出最小生成樹下一個結點:第index頂點 
        {
            if(closedge[v].lowcost<min && closedge[v].lowcost!=0 && closedge[v].lowcost!=INFINITY)
            {
                min = closedge[v].lowcost;
                index = v;
            }
        }
        sumw = sumw+closedge[index].lowcost;
        closedge[index].lowcost = 0;        //將第index個頂點併入U 

        for(j=0;j<G.vexnum;j++)
        {
            if(G.arcs[index][j]<closedge[j].lowcost)                //新頂點併入後重新選擇最小邊 
            {
                closedge[j].adjvex = G.vexs[index];
                closedge[j].lowcost = G.arcs[index][j];
            }
        }
    }
    //最小生成樹權值
    printf("%d\n",sumw);
 }

 int main()
 {
    MGraph mg;
    createMGraph(&mg);
    PRIM(mg,mg.vexs[0]);
 }

最小生成樹的性質:

       MST性質:假設G=(V,E)是一個連通網,U是頂點V的一個非空子集。若(u,v)是一條具有最小權值的邊,其中u∈U,v∈V-U,則必存在一棵包含邊(u,v)的最小生成樹。

 

構造網的最小生成樹必須解決下面兩個問題:

      (1)儘可能選取權值小的邊,但不能構成迴路;

      (2)選取n-1條恰當的邊以連通n個頂點;

 

prim演算法基本方法:       

        從連通網路N = { V, E }中的某一頂點u0出發,選擇與它關聯的具有最小權值的邊(u0, v),將其頂點加入到生成樹的頂點集合U中。以後每一步從一個頂點在U中,而另一個頂點不在U中的各條邊中選擇權值最小的邊(u, v),把該邊加入到生成樹的邊集TE中,把它的頂點加入到集合U中。如此重複執行,直到網路中的所有頂點都加入到生成樹頂點集合U中為止。 


假設G=(V,E)是一個具有n個頂點的帶權無向連通圖,T(U,TE)是G的最小生成樹,其中U是T的頂點集,TE是T的邊集,則構造G的最小生成樹T的步驟如下: 
(1)初始狀態,TE為空,U={v0},v0∈V; 
(2)在所有u∈U,v∈V-U的邊(u,v)∈E中找一條代價最小的邊(u′,v′)併入TE,同時將v′併入U; 
重複執行步驟(2)n-1次,直到U=V為止。