普里姆(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):代码输出
感谢与源代码
1:感谢一下博主文章对我的帮助:
https://blog.csdn.net/zguiz/article/details/54633115
2:源代码(VS2017)(这里说一下:VS工程有个.vs的隐藏配置文件,如果第一次弹出配置选错了,直接删除.vs,第二次打开会再次提示。VS向下兼容)
链接: https://pan.baidu.com/s/1Wv8uqTW7dU6mzinZSNCUgw 密码: p8nb