1.问题
在有n个结点的加权连通图内,求得最小生成树,即生成一个包含n个结点且所有边的权值最小的树。
2.解析
1).输入:一个加权连通图,其中顶点集合为V,边集合为E;
2).初始化:Vnew = {x},其中x为集合V中的任一节点(起始点),Enew = {},为空;
3).重复下列操作,直到Vnew = V:
- 在集合E中选取权值最小的边 <u, v>,其中u为集合Vnew中的元素,而v不在Vnew集合当中,并且v∈V(如果存在有多条满足前述条件即具有相同权值的边,则可任意选取其中之一);
- 将v加入集合Vnew中,将 <u, v> 边加入集合Enew中;
4).输出:使用集合Vnew和Enew来描述所得到的最小生成树。
Step | 图例 | 说明 | 可选顶点 | 已选顶点(Vnew) |
---|---|---|---|---|
1 | ![]() | 给定一个加权连通图,边上的数字表示权值 | - | - |
2 | ![]() | 随机选择起始顶点,例如顶点A。顶点B、C、D与顶点A相连,C与A的权值最小,因此选择顶点C与AC边并高亮表示 | B、C、D | A |
3 | ![]() | 顶点A、C与F、D、B相连,其中边CD权值为6最小,因此选择顶点D与CD边并高亮表示 | B、D、F | A、C |
4 | ![]() | 重复上面的步骤,找出权值最小的边BD,并将边和顶点B高亮表示 | B、E、F | A、C、D |
5 | ![]() | 这里只剩下E、F顶点,边BE的权值最小,因此选择BE | E、F | A、C、D、B |
6 | ![]() | 最后剩下点F,EF的权值最小,因此选择EF | F | A、C、D、B、E |
7 | ![]() | 现在,所有顶点均已被选取,图中绿色部分即为连通图的最小生成树。在此例中,最小生成树的权值之和为21 | - | A、C、D、B、E、F |
3.设计
#define NUM 20 //数组容量
#define INFINITY 65535 //象征无穷大
#define VRType int //权值类型
#define VertexType int //顶点类型
//存储数组
typedef struct {
VRType arcs[NUM][NUM]; //邻接矩阵map存储连通图,map[u][v]表示顶点u到v的权值
VertexType vexnum,arcnum; //记录图的顶点数和边数
}MGraph;
typedef struct {
VertexType adjvex;//记录权值最小的边的起始点
VRType lowcost;//记录该边的权值
}closedge[NUM];
//在辅助数组中找出权值最小的边的数组下标,就可以间接找到此边的终点顶点。
closedge dist;//创建一个全局数组,因为每个函数中都会使用到
mindist(MGraph G,closedge dist);
//start结点为初始结点
void Prim(MGraph G,VertexType start)
{
for(循环n次){
//遍历除start以外的结点,来记录与start相关的权值,没有和start连线的权值为无穷大
}
//由于起始点已经归为最小生成树,所以dist数组对应位置的权值为0,这样,遍历时就不会被选中
dist[start].lowcost=0;
for(循环n-1次)
{
//在dist数组找出权值最小的边,并记录下标pos
int pos=mindist(G,dist);
//归入最小生成树的顶点的dist数组中的权值设为0
dist[pos].locost=0;
//由于此时树中新加入了一个顶点,需要判断,由此顶点出发,到达其它各顶点的权值是否比之前记录的权值还要小,如果还小,则更新
for(int i=1;i<=n;i++){
//重点:为什么这个判断不会出现1-2、2-3、1-3的闭环情况?
//由于之前归入最小生成树的顶点的dist数组中的权值设为0
//设初始点为1,且1、2已为生成树内的点,3为新加入的点的话,1-2权值为0,1-3的权值为0
//i=1时,dist[1]为0<G.arcs[3][1]
//i=2时,dist[2]为1-2权值为0,G.arcs[3][2]为3-2权值>0,因此不会出现闭环情况
if(dist[i].lowcost>G.arcs[pos][i]){
dist[i].lowcost=G.arcs[pos][i];
}
}
}
}
4. 源码
https://github.com/QAQnoname/homework/blob/master/%E4%BD%9C%E4%B8%9A1/prim.c