Prim算法的思维路线较为简单,百度基本都能看懂。但是编程还是有点难的。
整个围绕两个东西转,一个lowcost数组,一个adjvex数组,前者是用来保存当前生成树到其它顶点的权值的。
比如这个图,假设一开始是从0出发,那么首先将0这个点并入到我们的生成树中,顺便设置一遍 lowcost数组,也就是,从0开始到其它顶点的权重。那么,这里有个问题,将0并入到我们的生成树,用什么标记表示这个顶点已经并入到我们的生成树中。这里选择的是用0!!也就是说,一开始,0并入,很好,我将它的lowcost[0]变成0表示这个顶点已经并入到生成树中了。那么我们的生成树到其它顶点的权重是什么呢?到1,是5,到3是无穷(这里的无穷写代码我们可以用一个比较大的数,比如65535),其它类推。那么这个adjvex数组是干嘛的呢?这只是方便我们输出的东西,表示的意思是(假设adjvex[1]是3,就表示当前树到1是通过顶点3的,也就是3->1是选的最小权重的边,如果不懂继续往下看)。
0 | 1 | 2 | 3 | 4 | 5 | 6 | |
lowcost | 0 | 5 | 11 | ∞ | ∞ | ∞ | ∞ |
adjvex | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
这是我们第一个顶点进入生成树中得到了,我们以这个为初始数据,然后才是真正的开始。
我们选择lowcost中最小的数据,并记录它的下标,比如说叫k。当前最小的是5这个权值,那么对应的下标是1,k=1,代码顶点是1,然后我们需要将这个1并入我们的生成树中,并且谁指向了1?是从0指向了1(0->1),所以,adjvex[1]=0的。同时,因为这个1顶点已经被并入到生成树中了,所以,需要将lowcost[1]=0。得到下表和对应代码
min = 65535;
j=0;k=0;
//选出权值最小的
while (j<G->vexnum){
if(lowcost[j]!=0&&lowcost[j]<min){
min = lowcost[j];
k=j;
}
j++;
}
printf("%d->%d ",adjvex[k],k);
lowcost[k]=0;//等于0的话代表这个顶点已经加入生成树 下次取权值不会在用到它。
0 | 1 | 2 | 3 | 4 | 5 | 6 | |
lowcost | 0 | 0 | 11 | ∞ | ∞ | ∞ | ∞ |
adjvex | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
然后 1进入我的生成树,我现在生成树中有{0,1}这两个点。这个新树到其它顶点的权重比老树(只有0这一个点时的树)发生了变化,比如说多了1->4,1->5,1->3,然后判断这些边的权重,是否比我们在lowcost数组中存放的更小,如果是的话就赋值更新。所以我们要更新lowcost数组,因为这个数组代表了当前生成树到其它顶点的权重啊。(比如说下面,我们的生成树到3原来是无穷,现在到3是12,当然小的多,毫不留情将它换成更小的),在转换过程中顺便将adjvex修改,表示是从什么顶点到这条边的。比如,
这里的生成树到3,4,5都是从1到它们的,所以都设置成1,那么你写程序怎么弄出这个1来呢?还记得上面选权值最小时那个小标吗,那个赋值给了k,此时我们只需要将更新过的(更新的条件就是lowcost不是0,不是0代表不在生成树中,而且新树比老树小的值,比如说是G->arc[k][j]<lowcost[j],此时的j是从0开始遍历到6,因为是邻接矩阵方式存储,需要将当前行就是第k行,所有数据和lowcost数组中的元素进行比较,如果lowcost数组中值比较大,就更新lowcost[j]=G->arc[k][j];,然后顺便将adjvex[j]=k;
这行无情的代码就写成了
//更新lowcost数组
for (j = 0; j<G->vexnum ; j++) {
if(lowcost[j]!=0&&G->arc[k][j]<lowcost[j]){
lowcost[j]=G->arc[k][j];
adjvex[j]=k;
}
}
0 | 1 | 2 | 3 | 4 | 5 | 6 | |
lowcost | 0 | 0 | 11 | 12 | 2 | 8 | ∞ |
adjvex | 0 | 0 | 0 | 1 | 1 | 1 | 0 |
之后,我们选出一个最小的lowcost中的权值,那就是2,对应的下标是4,k=4,此时,然后4这个顶点并入生成树了,所以,将lowcost[4]置为0,表示在生成树中了。
0 | 1 | 2 | 3 | 4 | 5 | 6 | |
lowcost | 0 | 0 | 11 | 12 | 0 | 8 | ∞ |
adjvex | 0 | 0 | 0 | 1 | 1 | 1 | 0 |
紧接着,因为4并入生成树,边又发生了改变,多了4->5和4->6,所以更新lowcost数组和adjvex数组。
4->5以前是8,现在一看,咦,顶点5有个权值4比8还小,换掉,成为了4,至于顶点6,呵呵权重,换掉,成为15。
在更新过程中,顺便更新adjvex,上面选最小权重选出来的顶点k=4,写入adjvex中,变成adjvex[5]=k,adjvex[6]=k。
0 | 1 | 2 | 3 | 4 | 5 | 6 | |
lowcost | 0 | 0 | 11 | 12 | 0 | 4 | 15 |
adjvex | 0 | 0 | 0 | 1 | 1 | 4 | 4 |
接着选出权值最小的,,一看就是5这个顶点了,并入生成树(lowcost[5]=0) k=5
0 | 1 | 2 | 3 | 4 | 5 | 6 | |
lowcost | 0 | 0 | 11 | 12 | 0 | 0 | 15 |
adjvex | 0 | 0 | 0 | 1 | 1 | 4 | 4 |
接着更新lowcost和adjvex。5->6的权重是3<15,更新lowcost[6]=3。顺便更新adjvex[6]=k=5
0 | 1 | 2 | 3 | 4 | 5 | 6 | |
lowcost | 0 | 0 | 11 | 12 | 0 | 0 | 3 |
adjvex | 0 | 0 | 0 | 1 | 1 | 4 | 5 |
后面就是一直 选最小,并入生成树(设为0),更新lowcost和adjvex。然后一直到这个几个顶点全部结束,我们是从0开始的,0的权重啥的做为了初值,因此,我们后面只要循环n-1次,这里7个顶点,就是7-1=6是,于是乎,变成了(i = 1; i<G->vexnum ; i++)
来看完整代码
#include <stdio.h>
#include <stdlib.h>
#define MaxVertexNum 7
#define MAX_VALUE 65535
//邻接矩阵
typedef char VertexType;
typedef int EdgeType;
//邻接矩阵
typedef struct MGraph{
VertexType vex[MaxVertexNum];//顶点数组
EdgeType arc[MaxVertexNum][MaxVertexNum];//二维边表
//vexnum代表顶点数目,arcnum代表边数
int vexnum,arcnum;
}MGraph;
//初始化一个邻接矩阵
MGraph *G = (MGraph *)malloc(sizeof(MGraph));
void initMGraph(MGraph *G){
G->vexnum=7;
G->arcnum=11;
for (int i = 0; i <7 ; ++i) {
G->vex[i]=i;
}
int arr[MaxVertexNum][MaxVertexNum] ={
{0,5,11,MAX_VALUE,MAX_VALUE,MAX_VALUE,MAX_VALUE},
{5,0,MAX_VALUE,12,2,8,MAX_VALUE},
{11,MAX_VALUE,0,24,MAX_VALUE,MAX_VALUE,20},
{MAX_VALUE,12,24,0,MAX_VALUE,MAX_VALUE,18},
{MAX_VALUE,2,MAX_VALUE,MAX_VALUE,0,4,15},
{MAX_VALUE,8,MAX_VALUE,MAX_VALUE,4,0,3},
{MAX_VALUE,MAX_VALUE,20,18,15,3,0}
};
for (int i = 0; i <MaxVertexNum ; ++i) {
for (int j = 0; j <MaxVertexNum ; ++j) {
G->arc[i][j]=arr[i][j];
}
}
}
//prim算法 王道版
void PRIM(MGraph *G){
int min,i,j,k;
int lowcost[MaxVertexNum];
int adjvex[MaxVertexNum];
lowcost[0]=0;
adjvex[0]=0;
for (i = 1; i < G->vexnum; ++i) {
lowcost[i]=G->arc[0][i];
adjvex[i]=0;
}
for (i = 1; i<G->vexnum ; i++) {
min = 65535;
j=0;k=0;
//选出权值最小的
while (j<G->vexnum){
if(lowcost[j]!=0&&lowcost[j]<min){
min = lowcost[j];
k=j;
}
j++;
}
printf("%d->%d ",adjvex[k],k);
lowcost[k]=0;//等于0的话代表这个顶点已经加入生成树 下次取权值不会在用到它。
//更新lowcost数组
for (j = 0; j<G->vexnum ; j++) {
if(lowcost[j]!=0&&G->arc[k][j]<lowcost[j]){
lowcost[j]=G->arc[k][j];
adjvex[j]=k;
}
}
}
}
int main(){
initMGraph(G);
PRIM(G);
}