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