图的邻接矩阵表示方式——最小生成树算法prim&&Kruskal

时间:2022-10-12 09:55:09
  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 }

图的邻接矩阵表示方式——最小生成树算法prim&&Kruskal

图的邻接矩阵表示方式——最小生成树算法prim&&Kruskal

 

 

参考 http://www.cnblogs.com/kangjianwei101/p/5222014.html

http://blog.csdn.net/u014488381/article/details/41447229