1. 程式人生 > >圖的陣列表示法(鄰接矩陣)與Prim演算法

圖的陣列表示法(鄰接矩陣)與Prim演算法

普里姆(Prim)演算法與最小生成樹

最小生成樹(MST)

對於連通網(帶權圖),選擇生成樹的總代價最少(Minimum Spanning Tree,MST ),比如一個N個城市之間的通訊網,網的頂點代表城市,邊代表這條路的修路費。那麼這樣就設計一個最小花費的問題。

最小生成樹可以由普里姆(Prim)演算法和Kruskal(克魯斯卡爾)演算法求出

一:普里姆(Prim)演算法

1:需要鄰接矩陣作為圖的儲存結構
2:需要一個輔助陣列來記錄從U到V-U的最小代價closedge[i]
3:這個U剛開始時候為空,逐個一個一個新增進U裡

(1):圖的鄰接矩陣
1:用兩個陣列分別表示(頂點)、(邊或弧)

注意事項:用65535來代表無窮大,表示兩個頂點沒有聯絡
儲存結構:

#define INT_MAX        65535                                        //最大值65535,表示兩頂點沒有聯絡
#define MAX_VERTEX_NUM 20                                           //最多頂點數

typedef char VertexType;
typedef int  EdgeType;

//有向圖, 有向網, 無向圖, 無向網
enum GraphKind {
    DG, DN, UDG, UDN
};

//頂點資訊
typedef struct ArcCell { EdgeType wight; }ArcCell, AdjMatrix[MAX_VERTEX_NUM][MAX_VERTEX_NUM]; //二維陣列 //弧的資訊 typedef struct MGraph { VertexType vexs[MAX_VERTEX_NUM]; //頂點向量 AdjMatrix arcs; //鄰接矩陣
int vexnum, arcnum; //圖的當前頂點數和弧數 GraphKind kind; //圖的種類標誌 }MGraph;
2:建立無向圖的鄰接矩陣

程式碼邏輯:
(1)輸入所有頂點
(2)輸入所有的邊
(3)將所有的邊都設定成65535,表示圖現在是孤立點,還沒有相互聯絡
(4)獲取輸入弧的兩個頂點在頂點陣列(鄰接矩陣的兩個陣列–頂點陣列,弧(邊)陣列)的位置
(5)由於是無向圖,且沒有使用矩陣的壓縮儲存,所以這裡對稱賦值
G.arcs[i][j].wight = G.arcs[j][i].wight = w;
注意事項:
這裡scanf的%c輸入是比較特殊的:
(1):需要用getchar()讀走空格
(2):scanf(” %c”, &G.vexs[i]); 新增一個空格(%c前有一個空格),讀走回車
這裡不詳細講了,具體在下面連結部落格(還不清楚,可以去看看):
https://blog.csdn.net/weixin_39956356/article/details/80371735
相關程式碼:

//建立無向圖的鄰接矩陣  
void CreatMGraph(MGraph &G)
{
    for (int i = 0; i < G.vexnum; i++) {
        printf("Please enter %d data:", i + 1);
        scanf(" %c", &G.vexs[i]);                                           //輸入頂點值
    }
    for (int i = 0; i<G.vexnum; i++)
        for (int j = 0; j<G.vexnum; j++)
            G.arcs[i][j].wight = INT_MAX;           //鄰接矩陣初始化  

    VertexType v1, v2;
    EdgeType w;
    int i, j;
    printf("Please input the two data of arc and wight,for example: A C 5\n");
    for (int k = 0; k < G.arcnum; k++) {
        printf("The %d arc: ", k + 1);
        scanf(" %c", &v1);                                                      //輸入第一個頂點
        getchar();                                                              //把輸入的空格讀走
        v2 = getchar();                                                         //輸入弧的第二個頂點
        scanf("%d", &w);                                                        //輸入結點數,弧數
        i = LocateVex(G, v1);                                                   //獲取弧的第一個節點位置
        j = LocateVex(G, v2);                                                   //獲取弧的第二個節點位置

        G.arcs[i][j].wight = G.arcs[j][i].wight = w;                            //把權值存放在鄰接矩陣中
    }
}
(2):Prim演算法描述

程式碼邏輯:
(1)輔助陣列初始化,把第一個點的鄰接矩陣拷貝到輔助陣列,將第一個頂點併入U集
輔助陣列結構:

//Prim演算法中的輔助資訊
struct prim{
    VertexType adjvex;                                              //存放最短路徑的出發節點結點
    EdgeType   lowcost;                                             //最短路徑
};

struct prim closedge[MAX_VERTEX_NUM];

(2)選擇其餘G.vexnum - 1個頂點,並選擇最小代價的邊
(3)輸出路徑與權值,將mincost併入U集
(4)如果有更小的邊,把小的替換原來的
選取書上的例子,最小樹建立過程:
這裡寫圖片描述
輔助陣列及變化:
這裡寫圖片描述
最短路徑及權值:
這裡寫圖片描述

普里姆演算法:

/************************************************************************
**                      普里姆演算法
**  輸入引數:MGraph &G          圖(鄰接矩陣表示)
            :VertexType u       任選一個頂點
**  返回引數:minSum         最短路徑之和
*************************************************************************/

int MiniSpanTree_Prim(MGraph &G, VertexType u)
{
    int k = LocateVex(G, u);                                                                            //確定第一個頂點的位置
    closedge[k].lowcost = 0;                                                                            //併入U集
    for (int i = 0; i < G.vexnum; i++)                                                                  //初始化輔助陣列
        if(i != k)
        { 
            closedge[i].adjvex = u;                                                                     //存入起始結點
            closedge[i].lowcost = G.arcs[k][i].wight;                                                   //鄰接矩陣該行拷貝到輔助陣列中
        }

    int minSum = 0;                                                                                     //最短路徑之和
    for (int i = 0; i < G.vexnum - 1; i++)                                                              //選擇其餘G.vexnum - 1個頂點
    {
        int mincost = minArc(G, closedge);                                                              //選擇最小代價的邊
        printf("%c--%c  %d\n", closedge[mincost].adjvex, G.vexs[mincost], closedge[mincost].lowcost);   //輸出路徑與權值
        minSum += closedge[mincost].lowcost;                                                            //最短路徑之和
        closedge[mincost].lowcost = 0;                                                                  //將mincost併入U集
        for (int j = 0; j < G.vexnum; j++)                                                              
        {
            if (G.arcs[mincost][j].wight < closedge[j].lowcost)                                         //如果有更小的邊,把小的替換原來的
            {
                closedge[j].adjvex = G.vexs[mincost];
                closedge[j].lowcost = G.arcs[mincost][j].wight;
            }
        }
    }
    return minSum;                                                                                      //返回最短路徑之和
}
(3):主函式
#include "stdafx.h"
#include "Prim.h"


int main()
{
    MGraph G;
    printf("Please enter vexnum and arcnum: ");
    scanf("%d %d", &G.vexnum, &G.arcnum);                                               //輸入結點數,弧數

    CreatMGraph(G);                                                                     //建立無向圖的鄰接矩陣 
    printf("\nTne output of Adjacency Matrix:\n\n");
    printMatrixGraph(G);                                                                //輸出鄰接矩陣
    printf("\nTne shortest path of Graph:\n\n");
    printf("\nThe sum of the shortest paths is %d.\n", MiniSpanTree_Prim(G, G.vexs[0]));//輸出路徑與權值

    return 0;
}
(4):程式碼輸出

這裡寫圖片描述