首先我们要知道普里姆算法是为了求最小二生成树。
这里不做过多介绍,直接上思想和代码。
一.具体过程:
(1)初始化U={v}。v到其他顶点的所有边为候选边;
(2)重复以下步骤n-1次,使得其他n-1个顶点被加入到U中:
a.从候选边中挑选权值最小的边输出,设该边在V-U中的顶点是k,将k加入U中;
b.考察当前V-U中的所有顶点j,修改候选边:若(j,k)的权值小于和和顶点j关联的候选边,则用(k,j)取代后者作为候选边。
上述过程大家可能看不懂,简单点讲就是先选取一个起始点a将a看成一个整体U,然后将其余的点看成另一个整体V-U,从和a邻接的边中选择一个权值最小的边,并加入这个边的结点,这两个结点和边就是新的U,然后再找与这个U相连的边中权值最小的边,并加入与此边相连的结点,重复上述步骤,将所有点加入即可构成最小生成树。
二.图解:
例如生成下图的最小生成树:
1.首先,我们选择起始点为结点0,与0邻接的边有(0,1),(0,5)。比较容易发现两者权值小的是10,所以加入结点5.得到如图:
2.将结点0和5以及权值为10的边(0,5)看成一个系统。找到与这个系统邻接的边(0,1),(5,4),比较两者的权值,容易发现权值最小的为25,因此加入边(5,4),同时加入结点4和边(5,4)。
3.将上图看成一个整体,它的邻接边有(0,1)28,(4,6)24,(4,3)22三者权值最小的为边(4,3),所以加入结点3.
4.将0,5,4,3以及相关的边看成一个整体,与其邻接的边有(0,1)28,(4,6)24,(3,6)18,(3,2)12,四个边中权值最小的边是(3,2),所以加入结点2以及边(3,2)。
5.与4中所构成的整体邻接的边有(0,1)28,(4,6)24,(3,6)18,(2,1)16,四者中权值最小的边为(2,1),所以加入结点1以及边(2,1)。
6.与5中所构成的整体邻接的边有(4,6)24,(3,6)18,(1,6)14,三者中权值最小的边为(1,6),所以加入结点6以及边(1,6)。至此所有点已经加入,也就生成了最小生成树。
那么我们如何用代码实现呢?
1.由于Prim算法中需要频繁的地取一条条边的权值,所以我们采用邻接矩阵更加合适。通过邻接矩阵我们不仅可以了解到结点与结点之间的关系,同时也可以了解权值的具体情况。
首先我们建立两个数组一个为lowcost[]:用于存储权值情况,另一个为closest[]:用于记录最小边的情况。我们规定lowcost[i]=0,表示已经加入了我们的整体中,即加入了U中。lowcost[i]!=0表示还没有加入,属于V-U中。当生成了最小生成树时,lowcost[]数组中全部为0.
2.还是以这个图为列:
它的邻接矩阵为:(这里我们用@代替无穷符号)
0 | 1 | 2 | 3 | 4 | 5 | 6 | |
0 | 0 | 28 | @ | @ | @ | 10 | @ |
1 | 28 | 0 | 16 | @ | @ | @ | 14 |
2 | @ | 16 | 0 | 12 | @ | @ | @ |
3 | @ | @ | 12 | 0 | 22 | @ | 18 |
4 | @ | @ | @ | 22 | 0 | 25 | 24 |
5 | 10 | @ | @ | @ | 25 | 0 | @ |
6 | @ | 14 | @ | 18 | 24 | @ | 0 |
图解执行过程(字可能有点小,丑,慢慢仔细看):
接下来就是具体代码执行过程:
在此之前我们先了解邻接矩阵的定义:
define MAXV<最大顶点个数>
#define INF 32767//定义无穷
typedef struct
{
int no; //顶点编号
InfoTYpe info; //顶点的其他信息(这里目前用不到)
}VertexType; //顶点的类型
typedef struct
{
int edges[MAXV][MAXV]; //邻接矩阵数组
int n,e; //顶点数,边数
VertexType vexs[MAXV]; //存放顶点信息
}MatGraph; //完整的图邻接矩阵类型
算法实现:
void Prim(MatGraph g,int v) //g是邻接矩阵,V是顶点编号
{
int lowcost[MAXV];
int mindist;
int closest[MAXV],i,j,k;
for(i=0;i<g.n;i++) //邻接矩阵中n是顶点数(这可以看邻接矩阵的定义)
{
lowcost[i]=g.edges[v][i];
closest[i]=v;
} //给数组lowcost[]与数组closest初始化
for(i=1;i<g.n;i++) //找出n-i个顶点
{
mindist=INF;
for(j=0;j<g.n;j++) //在(V-U)中找出离U最近的顶点
if(lowest[j]!=0&&lowest[j]<mindist)
{
mindist=lowcost[j];
k=j; //K记录最近的顶点的编号
}
printf("边(%d,%d)权为:%d\n",closest[k],k,mindist); //输出最小的一条生成边
lowcost[k]=0; //标记顶点K已经加入
for(j=0;j<g.n;j++) //对(V-U)中的顶点j进行调整
if(lowcost[j]!=0&&g.edges[k][j]<lowcost[j])
{
lowcost[j]=g.edges[k][j];
closest[j]=k; //修改数组lowcost[]和closest[]
}
}
}
Over!!!!!!