最小產生樹是資料結構中圖的一種重要應用,它的要求是從一個帶權無向完全圖中選擇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 6void 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); }}