普里姆算法(Prim算法),图论中的一种算法,可在加权连通图里搜索最小生成树。意即由此算法搜索到的边子集所构成的树中,不但包括了连通图里的所有顶点(英语:Vertex (graph theory)),且其所有边的权值之和亦为最小。
算法描述:
1).输入:一个加权连通图,其中顶点集合为V,边集合为E;
2).初始化:Vnew = {x},其中x为集合V中的任一节点(起始点),Enew = {},为空;
3).重复下列操作,直到Vnew = V:
a.在集合E中选取权值最小的边<u, v>,其中u为集合Vnew中的元素,而v不在Vnew集合当中,并且v∈V(如果存在有多条满足前述条件即具有相同权值的边,则可任意选取其中之一);
b.将v加入集合Vnew中,将<u, v>边加入集合Enew中;
4).输出:使用集合Vnew和Enew来描述所得到的最小生成树。
下面是普里姆算法的实现源代码,图的信息使用邻接表方式存储。有向图和无向图均可。
/* graph prim------adjacency List */
#include<stdlib.h>
#include<stdio.h>
#define MAX 20
/*弧结点信息*/
struct ArcNode{
int adjvex;/*该弧所指向的顶点的信息*/
int cost;
struct ArcNode *nextarc; /*指向下一条弧的指针*/
};
struct VexNode{
int data; /*顶点信息*/
int visited;/*顶点访问标志*/
struct ArcNode *firstarc;/*指向第一条依附于该顶点的弧的指针*/
};
struct VexNode head[MAX]; /*顶点数组*/
int vexnum=-1;
int graphtype;
int formatlist(){
printf("\n1---创建无向图:");
printf("\n2---创建有向图:");
printf("\n0---退出");
printf("\n请选择:");
scanf("%d",&graphtype);
return graphtype;
}
void constr(struct VexNode *head,int source, int destination,int cost){
int max;
struct ArcNode *newNode,*pointer;
newNode=(struct ArcNode*)malloc(sizeof(struct ArcNode));/*分配空间*/
if (newNode==NULL){
printf("分配节点出错!");
exit(0);
}
else{
newNode->adjvex=destination; /*生成弧结点*/
newNode->cost=cost;
newNode->nextarc=NULL;
if ( head[source].firstarc==NULL) /*头结点没有邻接点*/
head[source].firstarc=newNode;
else{
pointer=head[source].firstarc ; /*头结点已有邻接点*/
while ((pointer->adjvex!=destination)&&(pointer->nextarc!=NULL) )
pointer=pointer->nextarc; /*找到最后一个邻接点*/
if (pointer->adjvex==destination){
printf("\nthe input is duplicate!");
free(newNode);
}
else{
pointer->nextarc=newNode;/*接入*/
max=(source>destination)?source:destination; /*确定顶点的最大值*/
if (vexnum<max) vexnum=max;
}
}
}
}
void create_graph(struct VexNode *head){
int i,max,cost;
int source,destination;
struct ArcNode *newNode,*pointer;
for(i=1;i<MAX;++i){
head[i].data=i;
head[i].firstarc=NULL;
}
while(1){
printf("\n请输入弧的信息(起点 终点 权值(退出 for -1):");
scanf("%d%d%d",&source,&destination,&cost);
if (source==-1) break;
if (source==destination)
printf("\n 起点和终点相同!");
else{
constr(head,source,destination,cost);
if (graphtype==1) constr(head,destination,source,cost);
}
}/*while*/
}/*create_graph*/
void Print(struct VexNode * head){
int i,j;
struct ArcNode *pointer;
printf("\n=====当前图=====\n");
for (i=0;i<=vexnum;++i){
printf( " head[%d] : ",i);
pointer=head[i].firstarc ;
while (pointer!=NULL){
printf("[%d](%d) ", pointer->adjvex,pointer->cost);
pointer=pointer->nextarc;
}/*while*/
printf("\n");
}/*for*/
printf("\n===图形输出结束===\n");
}/*Print*/
void prim(struct VexNode * head){
int i,j;
int edgenum=0;
struct ArcNode *pointer;
int tempi,tempj,min=999;
for(i=1;i<=vexnum;i++)
head[i].visited=0;/*设置所有顶点均未被访问*/
printf("\n===普里姆算法:");
/*查找图中的第一条最小边*/
for(i=1;i<=vexnum;i++){
pointer=head[i].firstarc;
while(pointer){
if (pointer->cost<min){
min=pointer->cost;
tempi=i;tempj=pointer->adjvex;
}
pointer=pointer->nextarc;
}
}
printf("[%d,%d](%d)==>",tempi,tempj,min);
head[tempi].visited=1;
head[tempj].visited=1;
edgenum++;
while(edgenum<vexnum-1){
min=999;
/*查找下一条的最小边*/
for(i=1;i<=vexnum;i++){
if (head[i].visited==0)
continue;/*此边应从已访问的结点出发*/
pointer=head[i].firstarc;
while(pointer){
if (head[pointer->adjvex].visited==0&&pointer->cost<min){
min=pointer->cost;
tempi=i;tempj=pointer->adjvex;
}
pointer=pointer->nextarc;
}/*while*/
}/*for*/
printf("[%d,%d](%d)==>",tempi,tempj,min);
head[tempj].visited=1;/*标志为已访问*/
edgenum++;
}/*while*/
printf("普里姆算法结束\n");
}/*prim*/
main(){
int answer,search;
printf("\n===============================================");
printf("\n 普里姆算法");
printf("\n 邻接表表示 ");
printf("\n===============================================\n");
answer=formatlist();
switch(answer){
case 1:
case 2: create_graph(head);break;
case 0: exit(0);
default:{ printf("\n输入错误,请重新输入.\n");
answer=formatlist();}
}/*switch*/
Print(head);
prim(head);
system("pause");
exit(0);
}
对教材上的这个图进行运算
结果如下:
===============================================
普里姆算法
邻接表表示
===============================================
1---创建无向图:
2---创建有向图:
0---退出
请选择:1
请输入弧的信息(起点 终点 权值(退出 for -1):1 2 6
请输入弧的信息(起点 终点 权值(退出 for -1):1 3 1
请输入弧的信息(起点 终点 权值(退出 for -1):1 4 5
请输入弧的信息(起点 终点 权值(退出 for -1):2 3 5
请输入弧的信息(起点 终点 权值(退出 for -1):3 4 5
请输入弧的信息(起点 终点 权值(退出 for -1):2 5 3
请输入弧的信息(起点 终点 权值(退出 for -1):3 5 6
请输入弧的信息(起点 终点 权值(退出 for -1):3 6 4
请输入弧的信息(起点 终点 权值(退出 for -1):4 6 2
请输入弧的信息(起点 终点 权值(退出 for -1):5 6 6
请输入弧的信息(起点 终点 权值(退出 for -1):-1 -1 -1
=====当前图=====
head[0] :
head[1] : [2](6) [3](1) [4](5)
head[2] : [1](6) [3](5) [5](3)
head[3] : [1](1) [2](5) [4](5) [5](6) [6](4)
head[4] : [1](5) [3](5) [6](2)
head[5] : [2](3) [3](6) [6](6)
head[6] : [3](4) [4](2) [5](6)
===图形输出结束===
===普里姆算法:[1,3](1)==>[3,6](4)==>[6,4](2)==>[3,2](5)==>[2,5](3)==>普里姆算法
结束