1 #include<stdio.h> 2 #include<stdlib.h> 3 #define OK 1 4 #define NO 0 5 #define FALSE 0 6 #define TRUE 1 7 #define ERROR -1 8 9 #define INFINITY 200000 10 #define MAX_VERTEX_NUM 20 11 12 typedef int *SetType; 13 typedef int Status; 14 typedef int VRType;//圖、表頂點關系類型 15 typedef struct ArcCell 16 { 17 VRType adj; //權值 18 }ArcCell; 19 20 typedef ArcCell AdjMaxtrix[MAX_VERTEX_NUM+1][MAX_VERTEX_NUM+1];//鄰接矩陣 21 typedef char VertexType_M;//圖的頂點類型 22 23 24 typedef struct 25 { 26 VertexType_M vexs[MAX_VERTEX_NUM+1];//頂點向量 27 AdjMaxtrix arcs; 28 int vexnum,arcnum; 29 }MGraph; 30 31 //作用非常的重要,主要是為了實現記錄每一個結點的前一個結點以及它們之間的權重 32 typedef struct 33 { 34 VertexType_M adjvex;//較早加入當前邊的端點 35 VRType lowcost;//當前邊的權值 36 37 }Edge; 38 39 typedef struct //定義一個邊集用來存儲圖的所有邊信息 40 { 41 int begin,end; 42 int weight; 43 }gEdge; 44 45 46 Status visited[MAX_VERTEX_NUM+1]; 47 48 Status CreateUDN_M(MGraph *G); 49 50 void OutputMGraph(MGraph G); 51 52 int LocateVex_M(MGraph G,VertexType_M u); 53 54 int MinSpanTree_PRIM(MGraph G,VertexType_M u); 55 56 int Minimum(Edge closedge[],int n); 57 58 void MinSpanTree_KRUSKAL(MGraph G); 59 60 61 gEdge *CreateEdges(MGraph G);//生成圖的排序過的邊集數組 62 63 int main(int argc,char** argv){ 64 MGraph G; 65 printf("初始化並輸出無向網..\n"); 66 { 67 CreateUDN_M(&G); 68 69 OutputMGraph(G); 70 printf("\n"); 71 } 72 printf("函數 MinSpanTree_PRIM 測試..\n"); 73 { 74 int total; 75 76 printf("普里姆算法..\n"); 77 printf("先后加入最小生成樹的各結點及其對應的最小邊的權值分別為: \n"); 78 total=MinSpanTree_PRIM(G,'A'); 79 printf("\n"); 80 printf("輸出最小生成樹的總長度為%d\n",total); 81 printf("\n"); 82 } 83 printf("函數 MinSpanTree_KRUSKAL 測試..\n"); 84 { 85 printf("克魯斯卡爾算法..\n"); 86 printf("最小生成樹的各邊及其對應的權值分別為:\n"); 87 MinSpanTree_KRUSKAL(G); 88 printf("\n"); 89 } 90 91 92 return 0; 93 } 94 Status CreateUDN_M(MGraph *G){ 95 int i,j,k; 96 VertexType_M v1,v2; 97 VRType w; 98 printf("輸入頂點數 "); 99 scanf("%d",&(G->vexnum)); 100 printf("輸入弧數 "); 101 scanf("%d",&(G->arcnum)); 102 printf("輸入各個頂點值 "); 103 getchar(); 104 for(i=1;i<=G->vexnum;i++) 105 { 106 scanf("%c",&(G->vexs[i])); 107 } 108 109 printf("初始化鄰接矩陣\n"); 110 for(i=1;i<=G->vexnum;i++) 111 { 112 for(j=1;j<=G->vexnum;j++) 113 G->arcs[i][j].adj=INFINITY; 114 } 115 for(k=1;k<=G->arcnum;k++) 116 { 117 getchar(); 118 printf("輸入相鄰結點(添加弧的信息)"); 119 scanf("%c%c%d",&v1,&v2,&w); 120 i=LocateVex_M(*G,v1); 121 j=LocateVex_M(*G,v2); 122 123 G->arcs[i][j].adj=w; 124 G->arcs[j][i]=G->arcs[i][j]; 125 } 126 127 128 return OK; 129 } 130 131 int LocateVex_M(MGraph G,VertexType_M u) 132 { 133 int i; 134 for(i=1;i<=G.vexnum;i++) 135 { 136 if(G.vexs[i]==u) 137 return i; 138 } 139 return 0; 140 141 } 142 143 144 void OutputMGraph(MGraph G){ 145 int i,j; 146 if(!G.vexnum&&!G.arcnum) 147 printf("空圖(表)!\n"); 148 else 149 { 150 printf(" "); 151 for(i=1;i<=G.vexnum;i++) 152 { 153 printf("%3c",G.vexs[i]); 154 } 155 printf("\n"); 156 for(i=1;i<=G.vexnum;i++) 157 { 158 printf("%c",G.vexs[i]); 159 for(j=1;j<=G.vexnum;j++) 160 { 161 if(G.arcs[i][j].adj==INFINITY) 162 printf(" ∞"); 163 else 164 printf("%3d",G.arcs[i][j]); 165 } 166 printf("\n"); 167 168 } 169 } 170 } 171 172 /*Prim算法: 173 本質上是貪心算法,首先輸入一個頂點作為起點,提取這個頂點的序號,然后有一個closedge數組來初始化,遍歷之后將每個頂點的 174 前一個訪問的結點初始化為u,並且將他們之間的弧的權重直接當成二者之間的權重。然后首先輸出一個u.Minmum這個函數主要是用來 175 求出所有的邊中和u相連的弧的權重最小的頂點值(第一個循環的時候),然后輸出這個最小的權值以及頂點。並且將這個頂點的標記為 176 lowcast=0;在后面的訪問中就不會用到這個頂點。之后由於這個時候已知的頂點是兩個,因此對於所有和第二個結點中連接弧長最小 177 的頂點,需要改變弧的權值以及前一個頂點的位置。然后第二輪循環進來的時候,同樣在Minmum這個函數中實現找出所有哦沒有被訪問 178 過的權值最小的頂點,這個時候不需要擔心會訪問非前面兩個鄰接點,因為如果不是鄰接點,它們的弧的權重將是無窮大,因此肯定會 179 訪問權值最小的結點。然后循環將每一個結點都輸出。*/ 180 181 int MinSpanTree_PRIM(MGraph G,VertexType_M u){ 182 int i,j,k; 183 int total=0; 184 Edge closedge[G.vexnum+1]; 185 k=LocateVex_M(G,u); 186 /*將輔助矩陣初始化使每一個結點都標記為前一個結點是u,並且它們之間的距離是無窮大,若是鄰接點那么就是adj的值*/ 187 for(j=1;j<=G.vexnum;j++) 188 { 189 if(j!=k) 190 { 191 closedge[j].adjvex=u; //標記j這個頂點的前一個頂點的信息 192 closedge[j].lowcost=G.arcs[k][j].adj; 193 } 194 } 195 closedge[k].lowcost=0; //自己到自己的cost標記為0 196 printf(" %c\n", u); 197 for(i=1;i<=G.vexnum-1;i++)//總共需要這么多此找出最小邊 198 { 199 k=Minimum(closedge,G.vexnum); 200 total+=closedge[k].lowcost; 201 printf("%2d,%c\n",closedge[k].lowcost,G.vexs[k]); 202 closedge[k].lowcost=0; 203 for(j=1;j<=G.vexnum;j++) 204 { 205 if(G.arcs[k][j].adj<closedge[j].lowcost) 206 { 207 closedge[j].adjvex=G.vexs[k]; 208 closedge[j].lowcost=G.arcs[k][j].adj; 209 } 210 } 211 } 212 return total; 213 } 214 215 //找到所有和結點鄰接的點中權值最小的並且返回j(用來標記最小的值所在的下標) 216 int Minimum(Edge closedge[],int n){ 217 int i,j; 218 int min=INFINITY; 219 i=1; 220 j=0; 221 for(;i<=n;i++){ 222 if(closedge[i].lowcost)//從權值不為0的邊中選擇擁有最小權值的邊 223 { 224 if(closedge[i].lowcost<=min) 225 { 226 min=closedge[i].lowcost; 227 j=i; 228 } 229 } 230 } 231 return j; 232 } 233 234 gEdge *CreateEdges(MGraph G){ 235 int i,j; 236 int k=1; 237 int EdgeNum=G.arcnum; 238 int VertexNum=G.vexnum; 239 gEdge temp; 240 gEdge *p=(gEdge*)malloc(sizeof(gEdge)*VertexNum*VertexNum); //之前程序報錯 是因為申請的空間不夠大,越界了 241 242 for(i=1;i<=VertexNum;i++) //邊集數組初始化 243 for(j=i;j<=VertexNum;j++) //為了避免無向圖的每條邊被記錄兩次,只考慮上三角矩陣 244 if(G.arcs[i][j].adj!=0&&G.arcs[i][j].adj!=INFINITY){ 245 p[k].begin=i; 246 p[k].end=j; 247 p[k].weight=G.arcs[i][j].adj; 248 k++; 249 } 250 //首個p[i]與后面i+1……最后個依次比較,每一次都將小的放在第i個 251 for(i=1;i<EdgeNum;i++)//對邊集數組進行選擇排序 252 for(j=i+1;j<=EdgeNum;j++) 253 if(p[i].weight>p[j].weight){ 254 // temp.begin=p[i].begin; 255 // temp.end=p[i].end; 256 // temp.weight=p[i].weight; 257 258 259 temp=p[i]; 260 p[i]=p[j]; 261 p[j]=temp; 262 } 263 return p; //這個時候返回的p是已經從小到大排好序的,並且擁有一共EdgeNumt個大小的數組 264 } 265 266 void MinSpanTree_KRUSKAL(MGraph G){ 267 int VertexNum=G.vexnum; 268 int EdgeNum=G.arcnum; 269 gEdge *p=CreateEdges(G);//邊數組 270 int *index=(int*)malloc(sizeof(int)*VertexNum); 271 //index 數組,其元素為連通分量的編號,index[i]=index[j]表示編號為i,j的頂點在一個連通分量里 272 int *MSTEdge = (int *)malloc(sizeof(int)*VertexNum); //用來存儲已經確定的MST的邊的編號共VertexNum-1條邊 273 int k=1; 274 int WeightSum=0; 275 int IndexBegin,IndexEnd; 276 int i,j,n; 277 278 279 280 for(i=1;i<=VertexNum;i++) //初始化所有的index=-1; 281 index[i]=-1; 282 283 for(i=1;i<VertexNum;i++) 284 { 285 for(j=1;j<=EdgeNum;j++) 286 { 287 if(!(index[p[j].begin]>=0&&index[p[j].end]>=0&&index[p[j].begin]==index[p[j].end])){//找到當前還沒有加入到同一個連通分量權值最小的邊 288 MSTEdge[i]=j; //將每一個不同的弧復制給MSTEdge 一共有VertexNum條邊 289 if((index[p[j].begin]==-1)&&(index[p[j].end]==-1)) 290 index[p[j].begin]=index[p[j].end]=i;//將兩個點之間直接連通 291 else if((index[p[j].begin]==-1)&&(index[p[j].end]>=0)){ 292 index[p[j].begin]=i; 293 IndexEnd=index[p[j].end]; 294 for(n=1;n<=VertexNum;n++) 295 if(index[n]==IndexEnd) 296 index[n]=i; 297 //如果j這條弧中,有一個結點已經被連通,但是另一個結點還沒有被連通,那么這個時候因為這個時候 298 //要加入這一段弧,因此相當於將這兩個結點連接起來,因此我們就直接把,和另一個結點連接的所有的相同的結點都幅值為i 299 300 } 301 else if((index[p[j].end]==-1)&&(index[p[j].begin]>=0)) 302 { 303 index[p[j].end]=i; 304 IndexBegin=index[p[j].begin]; 305 for(n=1;n<=VertexNum;n++) 306 { 307 if(index[n]==IndexBegin) 308 index[n]=i; 309 } 310 311 } 312 else //也就是兩個結點都被包含進去了 但是還沒有連通 313 { 314 IndexEnd=index[p[j].end]; 315 IndexBegin=index[p[j].begin]; 316 for(n=1;n<=VertexNum;n++) 317 { 318 if(index[n]==IndexBegin||index[n]==IndexEnd) 319 index[n]=i; 320 } 321 322 } 323 break; 324 //里面執行完一次之后直接跳出循環,i進入下一條邊,而j仍然從0開始,而之前幅值過的邊不會再進去 325 //因此能夠保證進去的邊第一次,而對於新的邊中如果兩個結點已經是同一個集合中的元素,也不會再進去 326 327 } 328 } 329 } 330 331 332 333 334 printf("MTS的邊為:\n"); 335 for(i=1;i<VertexNum;i++){ 336 printf("%c--%c\n",G.vexs[p[MSTEdge[i]].begin],G.vexs[p[MSTEdge[i]].end]); 337 WeightSum+=p[MSTEdge[i]].weight; 338 } 339 printf("MST的值為:%d\n",WeightSum); 340 341 }


參考 http://www.cnblogs.com/kangjianwei101/p/5222014.html
http://blog.csdn.net/u014488381/article/details/41447229
