数据结构与算法 ~ 图 ~ 最小生成树 ~ 普里姆算法(图采用邻接表方式存储)

普里姆算法(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)==>普里姆算法
结束

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值