用邻接矩阵存储,并按Prim算法求最小生成树 实验
2.1 实验内容
根据书P262习题3给定的无向带权图,用邻接矩阵作为存储结构,用prim算法构造其最小生成树。
对图G(V,E)设置集合S,存放已访问的顶点,然后每次从集合V-S中选择与集合S的最短距离最小的一个顶点(记为u),访问并加入集合S。之后,令顶点u为中介点,优化所有从u能到达的顶点v与集合S之间的最短距离。执行n次(n为顶点个数),直到集合S已包含所有顶点。
2.2文件结构、开发环境等说明
开发环境版本 | VisualStudio 2022 | 工程文件名 | ex2_2_prim.sln |
头文件个数 | 3个 | 源程序文件个数 | 1个 |
文件名 | 文件类型 | 功能简介 | 备注 |
AdjMatrixUndirNetwork.h | 头文件 | 无向图的邻接矩阵类类模板头文件,包括数据成员及成员函数的声明与定义 | |
Prim.h | 头文件 | Prim算法实现最小生成树的构造 | |
Assistance.h | 头文件 | 辅助软件包 | |
TestPrim.cpp | 源文件 | 测试文件 |
2.3实现技术
1、邻接矩阵作为存储结构
在AdjMatrixUndirNetwork.h头文件中,用邻接矩阵类作为图的存储结构,该头文件中包含了邻接矩阵类的数据成员及成员函数的声明与定义(这里不做代码展示)。
2、辅助数组closearc[]
在利用普里姆算法构造最小生成树过程中,需要设置一个辅助数组closearc[ ],以记录从V-U中顶点到U中顶点具有最小权值的边。对每一个顶点v属于V-U,在辅助数组中有一个分量closearc[v],它包括两个域:lowweight和nearvertex。其中,lowwcight中存放顶点v到U中的各顶点的边上的当前最小权值(lowweight=0表示v属于U);nearvertex记录顶点v到U中具有最小权值的那条边的另一个邻接顶点u(nearvertex=-1表示该顶点v为开始顶点)。
template <class ElemType, class WeightType>
struct CloseArcType {
WeightType lowweight;
int nearvertex;
};
3、Prim算法
在普里姆算法中,主要有两个并列的for循环。设连通网络由n个顶点和e条边组成,则第一个for循环执行n-1次对辅助数组closear[]进行初始化;第二个for循环是个二重嵌套循环,外循环执行n-1次把n-1个顶点加入U中,对每加入一个顶点到U,有两个并列的for循环分别实现查找具有最小权值的边和修改辅助数组closearc[]。
在下面的普里姆算法描述中,连通网络采用邻接矩阵作为存储结构,并假设普里姆算法从顶点A(设项点A的序号为0)出发(即u=0)。普里姆算法步骤如下。
1)初始化辅助数组closearc[]。
2)重复下列步骤3)和4) n-1次。
3)在closearc[]中选择lowweight≠0&&lowweight最小的顶点v,即选中的权值最小的边为(closearc[v].nearvertex,i)。将closearc[v].lowweight改为0,表示顶点i已加入顶点集U中,并将边(closearc[v].ncarvertex,v)加入生成树T的边集合。
template <class ElemType, class WeightType>
void MiniSpanTreePrim(const AdjMatrixUndirNetwork<ElemType, WeightType>& g, int u0)// 初始条件:存在网g,u0为g的一个顶点
{// 操作结果:用Prim算法从u0出发构造网g的最小代价生成树
WeightType min;
ElemType v1, v2;
int vexnum = g.GetVexNum();
CloseArcType<ElemType, WeightType>* closearc;
if (u0 < 0 || u0 >= vexnum)
throw Error("顶点u0不存在!"); // 抛出异常
int u, v, k; // 表示顶点的临时变量
closearc = new CloseArcType<ElemType, WeightType>[vexnum]; // 分配存储空间
for (v = 0; v < vexnum; v++) { // 初始化辅助数组adjVex,并对顶点作标志,此时U = {v0}
closearc[v].nearvertex = u0;
closearc[v].lowweight = g.GetWeight(u0, v);
}
closearc[u0].nearvertex = -1;
closearc[u0].lowweight = 0;
for (k = 1; k < vexnum; k++) { // 选择生成树的其余g.GetVexNum() - 1个顶点
min = g.GetInfinity();
v = u0;// 选择使得边<w, adjVex[w]>为连接V-U到U的具有最小权值的边
for (u = 0; u < vexnum; u++)
if (closearc[u].lowweight != 0 && closearc[u].lowweight < min) {
v = u;
min = closearc[u].lowweight;
}
if (v != u0) {
g.GetElem(closearc[v].nearvertex, v1);
g.GetElem(v, v2);
cout << "边:( " << v1 << ", " << v2 << " ) 权:" << min << endl; // 输出边及权值
closearc[v].lowweight = 0; // 将w并入U
for (u = g.FirstAdjVex(v); u != -1; u = g.NextAdjVex(v, u)) // 新顶点并入U后重新选择最小边
if (closearc[u].lowweight != 0 && (g.GetWeight(v, u) < closearc[u].lowweight)) { // <v, w>为新的最小边
closearc[u].lowweight = g.GetWeight(v, u);
closearc[u].nearvertex = v;
}}}
delete[]closearc; } // 释放存储空间
4、测试代码
调用MiniSpanTreePrim(net, u0)函数,生成最小生成树(只展示重要部分)
cout << "从顶点A开始,利用Prim算法产生最小生成树的边:" << endl;
int u0 = 0;
MiniSpanTreePrim(net, u0); // Prim算法
cout << endl;
2.4测试结果
1、测试实例
2、用存储结构表示
3、Prim算法
完整代码可看资源区
创作不易~麻烦点个赞~~谢谢大家~~~