算法之prim算法
最小生成树是数据结构中图的一种重要应用,它的要求是从一个带权无向完全图中选择n-1条边并使这个图仍然连通(也即得到了一棵生成树),同时还要考虑使树的权最小。 prim算法就是一种最小生成树算法。
普里姆算法的基本思想:
从连通网N={V,E}中的某一顶点U0出发,选择与它关联的具有最小权值的边(U0,v),将其顶点加入到生成树的顶点集合U中。以后每一步从一个顶点在U中,而另一个顶点不在U中的各条边中选择权值最小的边(u,v),把它的顶点加入到集合U中。如此继续下去,直到网中的所有顶点都加入到生成树顶点集合U中为止。
下面举例说明下prim算法:
c语言实现如下:(使用邻接矩阵存储)
#include <stdio.h> #include <malloc.h> #define VERTEXNUM 6 void createGraph(int (*edge)[VERTEXNUM], int start, int end, int value); void displayGraph(int (*edge)[VERTEXNUM]); void prim(int (*edge)[VERTEXNUM], int (**tree)[VERTEXNUM], int startVertex, int* vertexStatusArr); int main(void){ //动态创建存放边的二维数组 int (*edge)[VERTEXNUM] = (int (*)[VERTEXNUM])malloc(sizeof(int)*VERTEXNUM*VERTEXNUM); int i,j; for(i=0;i<VERTEXNUM;i++){ for(j=0;j<VERTEXNUM;j++){ edge[i][j] = 0; } } //存放顶点的遍历状态,0:未遍历,1:已遍历 int* vertexStatusArr = (int*)malloc(sizeof(int)*VERTEXNUM); for(i=0;i<VERTEXNUM;i++){ vertexStatusArr[i] = 0; } printf("after init:\n"); displayGraph(edge); //创建图 createGraph(edge,0,1,6); createGraph(edge,0,3,5); createGraph(edge,0,2,1); createGraph(edge,1,2,5); createGraph(edge,1,4,3); createGraph(edge,2,4,6); createGraph(edge,2,3,5); createGraph(edge,2,5,4); createGraph(edge,3,5,2); createGraph(edge,4,5,6); printf("after create:\n"); displayGraph(edge); //最小生成树 int (*tree)[VERTEXNUM] = NULL; prim(edge, &tree, 0, vertexStatusArr); printf("after generate tree:\n"); displayGraph(tree); free(edge); free(tree); return 0; } //创建图 void createGraph(int (*edge)[VERTEXNUM], int start, int end, int value){ edge[start][end] = value; edge[end][start] = value; } //打印存储的图 void displayGraph(int (*edge)[VERTEXNUM]){ int i,j; for(i=0;i<VERTEXNUM;i++){ for(j=0;j<VERTEXNUM;j++){ printf("%d ",edge[i][j]); } printf("\n"); } } void prim(int (*edge)[VERTEXNUM], int (**tree)[VERTEXNUM], int startVertex, int* vertexStatusArr){ //申请存储树的内存 *tree = (int (*)[VERTEXNUM])malloc(sizeof(int)*VERTEXNUM*VERTEXNUM); int i,j; for(i=0;i<VERTEXNUM;i++){ for(j=0;j<VERTEXNUM;j++){ (*tree)[i][j] = 0; } } //从顶点0开始,则顶点0就是已访问的 vertexStatusArr[0] = 1; int least, start, end, vNum = 1; //如果还顶点还没有访问完 while(vNum < VERTEXNUM){ least = 9999; for(i=0;i<VERTEXNUM;i++){ //选择已经访问过的点 if(vertexStatusArr[i] == 1){ for(j=0;j<VERTEXNUM;j++){ //选择一个没有访问过的点 if(vertexStatusArr[j] == 0){ //选出一条value最小的边 if(edge[i][j] != 0 && edge[i][j] < least){ least = edge[i][j]; start = i; end = j; } } } } } vNum++; //将点设置为访问过 vertexStatusArr[end] = 1; //将边加到树中 createGraph(*tree,start,end,least); } }
c语言实现如下:(使用邻接表存储)
#include <stdio.h> #include <malloc.h> #define VERTEXNUM 6 //存放顶点的邻接表元素 typedef struct edge{ int vertex; int value; struct edge* next; }st_edge; void createGraph(st_edge** edge, int start, int end, int value); void displayGraph(st_edge** edge); void delGraph(st_edge** edge); void prim(st_edge** edge, st_edge*** tree, int startVertex, int* vertexStatusArr); int main(void){ //动态创建存放边的指针数组 st_edge** edge = (st_edge**)malloc(sizeof(st_edge*)*VERTEXNUM); int i; for(i=0;i<VERTEXNUM;i++){ edge[i] = NULL; } //存放顶点的遍历状态,0:未遍历,1:已遍历 int* vertexStatusArr = (int*)malloc(sizeof(int)*VERTEXNUM); for(i=0;i<VERTEXNUM;i++){ vertexStatusArr[i] = 0; } printf("after init:\n"); displayGraph(edge); //创建图 //从顶点0到顶点1的边,值为6,一下类推 createGraph(edge,0,1,6); createGraph(edge,0,3,5); createGraph(edge,0,2,1); createGraph(edge,1,2,5); createGraph(edge,1,4,3); createGraph(edge,2,4,6); createGraph(edge,2,3,5); createGraph(edge,2,5,4); createGraph(edge,3,5,2); createGraph(edge,4,5,6); printf("after create:\n"); displayGraph(edge); st_edge** tree = NULL; //从edge从中生成最小树tree,从顶点0开始 prim(edge, &tree, 0, vertexStatusArr); printf("after generate tree:\n"); displayGraph(tree); delGraph(edge); edge = NULL; delGraph(tree); tree = NULL; free(vertexStatusArr); vertexStatusArr = NULL; return 0; } //创建图 void createGraph(st_edge** edge, int start, int end, int value){ st_edge* newedge1 = (st_edge*)malloc(sizeof(st_edge)); newedge1->vertex = end; newedge1->value = value; newedge1->next = NULL; st_edge** edge1 = edge + start; while(*edge1 != NULL){ edge1 = &((*edge1)->next); } *edge1 = newedge1; st_edge* newedge2 = (st_edge*)malloc(sizeof(st_edge)); newedge2->vertex = start; newedge2->value = value; newedge2->next = NULL; st_edge** edge2 = edge + end; while(*edge2 != NULL){ edge2 = &((*edge2)->next); } *edge2 = newedge2; } //打印存储的图 void displayGraph(st_edge** edge){ int i; st_edge* p; for(i=0;i<VERTEXNUM;i++){ printf("%d:",i); p = *(edge+i); while(p != NULL){ printf("%d(%d) ",p->vertex,p->value); p = p->next; } printf("\n"); } } //释放邻接表占用的内存 void delGraph(st_edge** edge){ int i; st_edge* p; st_edge* del; for(i=0;i<VERTEXNUM;i++){ p = *(edge+i); while(p != NULL){ del = p; p = p->next; free(del); } edge[i] = NULL; } free(edge); } //prim算法 void prim(st_edge** edge, st_edge*** tree, int startVertex, int* vertexStatusArr){ //为最小生成树申请内存 *tree = (st_edge**)malloc(sizeof(st_edge*)*VERTEXNUM); int i,j; for(i=0;i<VERTEXNUM;i++){ (*tree)[i] = NULL; } //从顶点0开始,则顶点0就是已访问的 vertexStatusArr[0] = 1; st_edge* p; int least, start, end, vNum = 1; //如果还顶点还没有访问完 while(vNum < VERTEXNUM){ least = 9999; for(i=0;i<VERTEXNUM;i++){ //选择已经访问过的点 if(vertexStatusArr[i] == 1){ for(j=0;j<VERTEXNUM;j++){ //选择一个没有访问过的点 if(vertexStatusArr[j] == 0){ p = *(edge+i); //选出一条value最小的边 while(p != NULL){ if(p->value < least && p->vertex == j){ least = p->value; start = i; end = j; } p = p->next; } } } } } vNum++; //将点设置为访问过 vertexStatusArr[end] = 1; //将边加到树中 createGraph(*tree,start,end,least); } }