圖的陣列表示法(鄰接矩陣)與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;
}