1. 程式人生 > 實用技巧 >最小生成樹之Prim演算法

最小生成樹之Prim演算法

生成樹

 一個連通圖的生成樹是一個極小連通子圖,它含有圖中全部n個頂點和構成一棵樹的(n-1)條邊。  
  • 連通圖由一次遍歷就可以產生生成樹
  • 由深度優先遍歷得到的生成樹稱為深度優先生成樹。
  • 由廣度優先遍歷得到的生成樹稱為廣度優先生成樹。

一個連通圖的生成樹不一定是唯一的!

最小生成樹

對於帶權連通圖G (每條邊上的權均為大於零的實數),可能有多棵不同生成樹。
每棵生成樹的所有邊的權值之和可能不同。
其中權值之和最小的生成樹稱為圖的最小生成樹。

Prim演算法

普里姆(Prim)演算法是一種構造性演算法,用於構造最小生成樹。過程如下:

  1. 初始化U={v}。v到其他頂點的所有邊為候選邊;
  2. 重複以下步驟n-1次,使得其他n-1個頂點被加入到U中:
  3. 從候選邊中挑選權值最小的邊輸出,設該邊在V-U中的頂點是k,將k加入U中;
  4. 考察當前V-U中的所有頂點j,修改候選邊:若(j,k)的權值小於原來和頂點k關聯的候選邊,則用(k,j)取代後者作為候選邊。

程式碼

Prim 演算法構造生成樹

//Prim 演算法構造生成樹
void Prim(MatGraph g, int v)
{
    int lowcost[MAXV];//儲存權值
    int MIN;
    int closest[MAXV], i, j, k;
//closet是用來儲存與它相鄰的節點的
    for (i = 0; i < g.n; i++)
    {
        lowcost[i] = g.edges[v][i]; //初始化
        closest[i] = v;
    }
    for (i = 1; i < g.n; i++)
    {
        MIN = INF;
        for (j = 0; j < g.n; j++)
            if (lowcost[j] != 0 && lowcost[j] < MIN)
            {
                MIN = lowcost[j];
                k = j; //記錄最近的節點的編號
            }
        printf("邊(%d,%d)權為:%d\n", closest[k], k, MIN);
        lowcost[k] = 0;

        for (j = 0; j < g.n; j++)
            if (lowcost[j] != 0 && g.edges[k][j] < lowcost[j]) //尋找有沒有比較小的邊
            {
                lowcost[j] = g.edges[k][j];
                closest[j] = k;
            }
    }
}

測試程式碼

# include <stdio.h>
# include <stdlib.h>
#define ElemType int
#define maxsize 100
#define InfoType int
#define MAXV 100
#define MaxSize 100
#define INF 214748364 
#define INFINITE INF
//鄰接矩陣的資料型別
typedef struct s
{
    int no;     //頂點編號
    InfoType info;//頂點的其他資訊
} VertexType;      //頂點的型別

typedef struct SS
{
    int edges[MAXV][MAXV]; //鄰接矩陣的陣列
    int n, e;              //圖的頂點數和邊數
    VertexType vexs[MAXV]; //存放頂點資訊
} MatGraph;
///////////////////////////////////////////////////

//Prim 演算法構造生成樹
void Prim(MatGraph g, int v)
{
    int lowcost[MAXV];
    int MIN;
    int closest[MAXV], i, j, k;

    for (i = 0; i < g.n; i++)
    {
        lowcost[i] = g.edges[v][i]; //init
        closest[i] = v;
    }
    for (i = 1; i < g.n; i++)
    {
        MIN = INF;
        for (j = 0; j < g.n; j++)
            if (lowcost[j] != 0 && lowcost[j] < MIN)
            {
                MIN = lowcost[j];
                k = j; //記錄最近的節點的編號
            }
        printf("邊(%d,%d)權為:%d\n", closest[k], k, MIN);
        lowcost[k] = 0;

        for (j = 0; j < g.n; j++)
            if (lowcost[j] != 0 && g.edges[k][j] < lowcost[j])
            {
                lowcost[j] = g.edges[k][j];
                closest[j] = k;
            }
    }
}
//////////////////////////////////////////////////////////////
void InitMatGraph(MatGraph & g, int a[][MAXV], int n, int e)
{
    int i, j;
    g.n = n;
    g.e = e;
    for(i = 0; i< n; i++)
        for(j = 0; j < n; j++)
            g.edges[i][j] = a[i][j];
}


int main ()
{
    //注意無向帶權圖i=j是為0
        int a[4][MAXV] = {{0, 1, 3, 1},
                          {1, 0, 2, 4},
                          {3, 2, 0, INF},
                          {1, 4, INF, 0}};
        MatGraph  g;

        InitMatGraph(g, a, 4, 5);

        Prim(g, 0);
        return 0;
}

測試結果

Enjoy