圖的鄰接矩陣表示方式——最小生成樹算法prim&&Kruskal


  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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM