图的全部实现(邻接矩阵 邻接表 BFS DFS 最小生成树 最短路径等)
- 1 /**
- 2 * C: Dijkstra算法获取最短路径(邻接矩阵)
- 3 *
6 */- 7
- 8 #include <stdio.h>
- 9 #include <stdlib.h>
- 10 #include <malloc.h>
- 11 #include <string.h>
- 12
- 13 #define MAX 100 // 矩阵最大容量
- 14 #define INF (~(0x1<<31)) // 最大值(即0X7FFFFFFF)
- 15 #define isLetter(a) ((((a)>='a')&&((a)<='z')) || (((a)>='A')&&((a)<='Z')))
- 16 #define LENGTH(a) (sizeof(a)/sizeof(a[0]))
- 17
- 18 // 邻接矩阵
- 19 typedef struct _graph
- 20 {
- 21 char vexs[MAX]; // 顶点集合
- 22 int vexnum; // 顶点数
- 23 int edgnum; // 边数
- 24 int matrix[MAX][MAX]; // 邻接矩阵
- 25 }Graph, *PGraph;
- 26
- 27 // 边的结构体
- 28 typedef struct _EdgeData
- 29 {
- 30 char start; // 边的起点
- 31 char end; // 边的终点
- 32 int weight; // 边的权重
- 33 }EData;
- 34
- 35 /*
- 36 * 返回ch在matrix矩阵中的位置
- 37 */
- 38 static int get_position(Graph G, char ch)
- 39 {
- 40 int i;
- 41 for(i=0; i<G.vexnum; i++)
- 42 if(G.vexs[i]==ch)
- 43 return i;
- 44 return -1;
- 45 }
- 46
- 47 /*
- 48 * 读取一个输入字符
- 49 */
- 50 static char read_char()
- 51 {
- 52 char ch;
- 53
- 54 do {
- 55 ch = getchar();
- 56 } while(!isLetter(ch));
- 57
- 58 return ch;
- 59 }
- 60
- 61 /*
- 62 * 创建图(自己输入)
- 63 */
- 64 Graph* create_graph()
- 65 {
- 66 char c1, c2;
- 67 int v, e;
- 68 int i, j, weight, p1, p2;
- 69 Graph* pG;
- 70
- 71 // 输入"顶点数"和"边数"
- 72 printf("input vertex number: ");
- 73 scanf("%d", &v);
- 74 printf("input edge number: ");
- 75 scanf("%d", &e);
- 76 if ( v < 1 || e < 1 || (e > (v * (v-1))))
- 77 {
- 78 printf("input error: invalid parameters!\n");
- 79 return NULL;
- 80 }
- 81
- 82 if ((pG=(Graph*)malloc(sizeof(Graph))) == NULL )
- 83 return NULL;
- 84 memset(pG, 0, sizeof(Graph));
- 85
- 86 // 初始化"顶点数"和"边数"
- 87 pG->vexnum = v;
- 88 pG->edgnum = e;
- 89 // 初始化"顶点"
- 90 for (i = 0; i < pG->vexnum; i++)
- 91 {
- 92 printf("vertex(%d): ", i);
- 93 pG->vexs[i] = read_char();
- 94 }
- 95
- 96 // 1. 初始化"边"的权值
- 97 for (i = 0; i < pG->vexnum; i++)
- 98 {
- 99 for (j = 0; j < pG->vexnum; j++)
- 100 {
- 101 if (i==j)
- 102 pG->matrix[i][j] = 0;
- 103 else
- 104 pG->matrix[i][j] = INF;
- 105 }
- 106 }
- 107 // 2. 初始化"边"的权值: 根据用户的输入进行初始化
- 108 for (i = 0; i < pG->edgnum; i++)
- 109 {
- 110 // 读取边的起始顶点,结束顶点,权值
- 111 printf("edge(%d):", i);
- 112 c1 = read_char();
- 113 c2 = read_char();
- 114 scanf("%d", &weight);
- 115
- 116 p1 = get_position(*pG, c1);
- 117 p2 = get_position(*pG, c2);
- 118 if (p1==-1 || p2==-1)
- 119 {
- 120 printf("input error: invalid edge!\n");
- 121 free(pG);
- 122 return NULL;
- 123 }
- 124
- 125 pG->matrix[p1][p2] = weight;
- 126 pG->matrix[p2][p1] = weight;
- 127 }
- 128
- 129 return pG;
- 130 }
- 131
- 132 /*
- 133 * 创建图(用已提供的矩阵)
- 134 */
- 135 Graph* create_example_graph()
- 136 {
- 137 char vexs[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G'};
- 138 int matrix[][9] = {
- 139 /*A*//*B*//*C*//*D*//*E*//*F*//*G*/
- 140 /*A*/ { 0, 12, INF, INF, INF, 16, 14},
- 141 /*B*/ { 12, 0, 10, INF, INF, 7, INF},
- 142 /*C*/ { INF, 10, 0, 3, 5, 6, INF},
- 143 /*D*/ { INF, INF, 3, 0, 4, INF, INF},
- 144 /*E*/ { INF, INF, 5, 4, 0, 2, 8},
- 145 /*F*/ { 16, 7, 6, INF, 2, 0, 9},
- 146 /*G*/ { 14, INF, INF, INF, 8, 9, 0}};
- 147 int vlen = LENGTH(vexs);
- 148 int i, j;
- 149 Graph* pG;
- 150
- 151 // 输入"顶点数"和"边数"
- 152 if ((pG=(Graph*)malloc(sizeof(Graph))) == NULL )
- 153 return NULL;
- 154 memset(pG, 0, sizeof(Graph));
- 155
- 156 // 初始化"顶点数"
- 157 pG->vexnum = vlen;
- 158 // 初始化"顶点"
- 159 for (i = 0; i < pG->vexnum; i++)
- 160 pG->vexs[i] = vexs[i];
- 161
- 162 // 初始化"边"
- 163 for (i = 0; i < pG->vexnum; i++)
- 164 for (j = 0; j < pG->vexnum; j++)
- 165 pG->matrix[i][j] = matrix[i][j];
- 166
- 167 // 统计边的数目
- 168 for (i = 0; i < pG->vexnum; i++)
- 169 for (j = 0; j < pG->vexnum; j++)
- 170 if (i!=j && pG->matrix[i][j]!=INF)
- 171 pG->edgnum++;
- 172 pG->edgnum /= 2;
- 173
- 174 return pG;
- 175 }
- 176
- 177 /*
- 178 * 返回顶点v的第一个邻接顶点的索引,失败则返回-1
- 179 */
- 180 static int first_vertex(Graph G, int v)
- 181 {
- 182 int i;
- 183
- 184 if (v<0 || v>(G.vexnum-1))
- 185 return -1;
- 186
- 187 for (i = 0; i < G.vexnum; i++)
- 188 if (G.matrix[v][i]!=0 && G.matrix[v][i]!=INF)
- 189 return i;
- 190
- 191 return -1;
- 192 }
- 193
- 194 /*
- 195 * 返回顶点v相对于w的下一个邻接顶点的索引,失败则返回-1
- 196 */
- 197 static int next_vertix(Graph G, int v, int w)
- 198 {
- 199 int i;
- 200
- 201 if (v<0 || v>(G.vexnum-1) || w<0 || w>(G.vexnum-1))
- 202 return -1;
- 203
- 204 for (i = w + 1; i < G.vexnum; i++)
- 205 if (G.matrix[v][i]!=0 && G.matrix[v][i]!=INF)
- 206 return i;
- 207
- 208 return -1;
- 209 }
- 210
- 211 /*
- 212 * 深度优先搜索遍历图的递归实现
- 213 */
- 214 static void DFS(Graph G, int i, int *visited)
- 215 {
- 216 int w;
- 217
- 218 visited[i] = 1;
- 219 printf("%c ", G.vexs[i]);
- 220 // 遍历该顶点的所有邻接顶点。若是没有访问过,那么继续往下走
- 221 for (w = first_vertex(G, i); w >= 0; w = next_vertix(G, i, w))
- 222 {
- 223 if (!visited[w])
- 224 DFS(G, w, visited);
- 225 }
- 226
- 227 }
- 228
- 229 /*
- 230 * 深度优先搜索遍历图
- 231 */
- 232 void DFSTraverse(Graph G)
- 233 {
- 234 int i;
- 235 int visited[MAX]; // 顶点访问标记
- 236
- 237 // 初始化所有顶点都没有被访问
- 238 for (i = 0; i < G.vexnum; i++)
- 239 visited[i] = 0;
- 240
- 241 printf("DFS: ");
- 242 for (i = 0; i < G.vexnum; i++)
- 243 {
- 244 //printf("\n== LOOP(%d)\n", i);
- 245 if (!visited[i])
- 246 DFS(G, i, visited);
- 247 }
- 248 printf("\n");
- 249 }
- 250
- 251 /*
- 252 * 广度优先搜索(类似于树的层次遍历)
- 253 */
- 254 void BFS(Graph G)
- 255 {
- 256 int head = 0;
- 257 int rear = 0;
- 258 int queue[MAX]; // 辅组队列
- 259 int visited[MAX]; // 顶点访问标记
- 260 int i, j, k;
- 261
- 262 for (i = 0; i < G.vexnum; i++)
- 263 visited[i] = 0;
- 264
- 265 printf("BFS: ");
- 266 for (i = 0; i < G.vexnum; i++)
- 267 {
- 268 if (!visited[i])
- 269 {
- 270 visited[i] = 1;
- 271 printf("%c ", G.vexs[i]);
- 272 queue[rear++] = i; // 入队列
- 273 }
- 274 while (head != rear)
- 275 {
- 276 j = queue[head++]; // 出队列
- 277 for (k = first_vertex(G, j); k >= 0; k = next_vertix(G, j, k)) //k是为访问的邻接顶点
- 278 {
- 279 if (!visited[k])
- 280 {
- 281 visited[k] = 1;
- 282 printf("%c ", G.vexs[k]);
- 283 queue[rear++] = k;
- 284 }
- 285 }
- 286 }
- 287 }
- 288 printf("\n");
- 289 }
- 290
- 291 /*
- 292 * 打印矩阵队列图
- 293 */
- 294 void print_graph(Graph G)
- 295 {
- 296 int i,j;
- 297
- 298 printf("Martix Graph:\n");
- 299 for (i = 0; i < G.vexnum; i++)
- 300 {
- 301 for (j = 0; j < G.vexnum; j++)
- 302 printf("%10d ", G.matrix[i][j]);
- 303 printf("\n");
- 304 }
- 305 }
- 306
- 307 /*
- 308 * prim最小生成树
- 309 *
- 310 * 参数说明:
- 311 * G -- 邻接矩阵图
- 312 * start -- 从图中的第start个元素开始,生成最小树
- 313 */
- 314 void prim(Graph G, int start)
- 315 {
- 316 int min,i,j,k,m,n,sum;
- 317 int index=0; // prim最小树的索引,即prims数组的索引
- 318 char prims[MAX]; // prim最小树的结果数组
- 319 int weights[MAX]; // 顶点间边的权值
- 320
- 321 // prim最小生成树中第一个数是"图中第start个顶点",因为是从start开始的。
- 322 prims[index++] = G.vexs[start];
- 323
- 324 // 初始化"顶点的权值数组",
- 325 // 将每个顶点的权值初始化为"第start个顶点"到"该顶点"的权值。
- 326 for (i = 0; i < G.vexnum; i++ )
- 327 weights[i] = G.matrix[start][i];
- 328 // 将第start个顶点的权值初始化为0。
- 329 // 可以理解为"第start个顶点到它自身的距离为0"。
- 330 weights[start] = 0;
- 331
- 332 for (i = 0; i < G.vexnum; i++)
- 333 {
- 334 // 由于从start开始的,因此不需要再对第start个顶点进行处理。
- 335 if(start == i)
- 336 continue;
- 337
- 338 j = 0;
- 339 k = 0;
- 340 min = INF;
- 341 // 在未被加入到最小生成树的顶点中,找出权值最小的顶点。
- 342 while (j < G.vexnum)
- 343 {
- 344 // 若weights[j]=0,意味着"第j个节点已经被排序过"(或者说已经加入了最小生成树中)。
- 345 if (weights[j] != 0 && weights[j] < min)
- 346 {
- 347 min = weights[j];
- 348 k = j;
- 349 }
- 350 j++;
- 351 }
- 352
- 353 // 经过上面的处理后,在未被加入到最小生成树的顶点中,权值最小的顶点是第k个顶点。
- 354 // 将第k个顶点加入到最小生成树的结果数组中
- 355 prims[index++] = G.vexs[k];
- 356 // 将"第k个顶点的权值"标记为0,意味着第k个顶点已经排序过了(或者说已经加入了最小树结果中)。
- 357 weights[k] = 0;
- 358 // 当第k个顶点被加入到最小生成树的结果数组中之后,更新其它顶点的权值。
- 359 for (j = 0 ; j < G.vexnum; j++)
- 360 {
- 361 // 当第j个节点没有被处理,并且需要更新时才被更新。
- 362 if (weights[j] != 0 && G.matrix[k][j] < weights[j])
- 363 weights[j] = G.matrix[k][j];
- 364 }
- 365 }
- 366
- 367 // 计算最小生成树的权值
- 368 sum = 0;
- 369 for (i = 1; i < index; i++)
- 370 {
- 371 min = INF;
- 372 // 获取prims[i]在G中的位置
- 373 n = get_position(G, prims[i]);
- 374 // 在vexs[0...i]中,找出到j的权值最小的顶点。
- 375 for (j = 0; j < i; j++)
- 376 {
- 377 m = get_position(G, prims[j]);
- 378 if (G.matrix[m][n]<min)
- 379 min = G.matrix[m][n];
- 380 }
- 381 sum += min;
- 382 }
- 383 // 打印最小生成树
- 384 printf("PRIM(%c)=%d: ", G.vexs[start], sum);
- 385 for (i = 0; i < index; i++)
- 386 printf("%c ", prims[i]);
- 387 printf("\n");
- 388 }
- 389
- 390 /*
- 391 * 获取图中的边
- 392 */
- 393 EData* get_edges(Graph G)
- 394 {
- 395 int i,j;
- 396 int index=0;
- 397 EData *edges;
- 398
- 399 edges = (EData*)malloc(G.edgnum*sizeof(EData));
- 400 for (i=0;i < G.vexnum;i++)
- 401 {
- 402 for (j=i+1;j < G.vexnum;j++)
- 403 {
- 404 if (G.matrix[i][j]!=INF)
- 405 {
- 406 edges[index].start = G.vexs[i];
- 407 edges[index].end = G.vexs[j];
- 408 edges[index].weight = G.matrix[i][j];
- 409 index++;
- 410 }
- 411 }
- 412 }
- 413
- 414 return edges;
- 415 }
- 416
- 417 /*
- 418 * 对边按照权值大小进行排序(由小到大)
- 419 */
- 420 void sorted_edges(EData* edges, int elen)
- 421 {
- 422 int i,j;
- 423
- 424 for (i=0; i<elen; i++)
- 425 {
- 426 for (j=i+1; j<elen; j++)
- 427 {
- 428 if (edges[i].weight > edges[j].weight)
- 429 {
- 430 // 交换"第i条边"和"第j条边"
- 431 EData tmp = edges[i];
- 432 edges[i] = edges[j];
- 433 edges[j] = tmp;
- 434 }
- 435 }
- 436 }
- 437 }
- 438
- 439 /*
- 440 * 获取i的终点
- 441 */
- 442 int get_end(int vends[], int i)
- 443 {
- 444 while (vends[i] != 0)
- 445 i = vends[i];
- 446 return i;
- 447 }
- 448
- 449 /*
- 450 * 克鲁斯卡尔(Kruskal)最小生成树
- 451 */
- 452 void kruskal(Graph G)
- 453 {
- 454 int i,m,n,p1,p2;
- 455 int length;
- 456 int index = 0; // rets数组的索引
- 457 int vends[MAX]={0}; // 用于保存"已有最小生成树"中每个顶点在该最小树中的终点。
- 458 EData rets[MAX]; // 结果数组,保存kruskal最小生成树的边
- 459 EData *edges; // 图对应的所有边
- 460
- 461 // 获取"图中所有的边"
- 462 edges = get_edges(G);
- 463 // 将边按照"权"的大小进行排序(从小到大)
- 464 sorted_edges(edges, G.edgnum);
- 465
- 466 for (i=0; i<G.edgnum; i++)
- 467 {
- 468 p1 = get_position(G, edges[i].start); // 获取第i条边的"起点"的序号
- 469 p2 = get_position(G, edges[i].end); // 获取第i条边的"终点"的序号
- 470
- 471 m = get_end(vends, p1); // 获取p1在"已有的最小生成树"中的终点
- 472 n = get_end(vends, p2); // 获取p2在"已有的最小生成树"中的终点
- 473 // 如果m!=n,意味着"边i"与"已经添加到最小生成树中的顶点"没有形成环路
- 474 if (m != n)
- 475 {
- 476 vends[m] = n; // 设置m在"已有的最小生成树"中的终点为n
- 477 rets[index++] = edges[i]; // 保存结果
- 478 }
- 479 }
- 480 free(edges);
- 481
- 482 // 统计并打印"kruskal最小生成树"的信息
- 483 length = 0;
- 484 for (i = 0; i < index; i++)
- 485 length += rets[i].weight;
- 486 printf("Kruskal=%d: ", length);
- 487 for (i = 0; i < index; i++)
- 488 printf("(%c,%c) ", rets[i].start, rets[i].end);
- 489 printf("\n");
- 490 }
- 491
- 492 /*
- 493 * Dijkstra最短路径。
- 494 * 即,统计图(G)中"顶点vs"到其它各个顶点的最短路径。
- 495 *
- 496 * 参数说明:
- 497 * G -- 图
- 498 * vs -- 起始顶点(start vertex)。即计算"顶点vs"到其它顶点的最短路径。
- 499 * prev -- 前驱顶点数组。即,prev[i]的值是"顶点vs"到"顶点i"的最短路径所经历的全部顶点中,位于"顶点i"之前的那个顶点。
- 500 * dist -- 长度数组。即,dist[i]是"顶点vs"到"顶点i"的最短路径的长度。
- 501 */
- 502 void dijkstra(Graph G, int vs, int prev[], int dist[])
- 503 {
- 504 int i,j,k;
- 505 int min;
- 506 int tmp;
- 507 int flag[MAX]; // flag[i]=1表示"顶点vs"到"顶点i"的最短路径已成功获取。
- 508
- 509 // 初始化
- 510 for (i = 0; i < G.vexnum; i++)
- 511 {
- 512 flag[i] = 0; // 顶点i的最短路径还没获取到。
- 513 prev[i] = 0; // 顶点i的前驱顶点为0。
- 514 dist[i] = G.matrix[vs][i];// 顶点i的最短路径为"顶点vs"到"顶点i"的权。
- 515 }
- 516
- 517 // 对"顶点vs"自身进行初始化
- 518 flag[vs] = 1;
- 519 dist[vs] = 0;
- 520
- 521 // 遍历G.vexnum-1次;每次找出一个顶点的最短路径。
- 522 for (i = 1; i < G.vexnum; i++)
- 523 {
- 524 // 寻找当前最小的路径;
- 525 // 即,在未获取最短路径的顶点中,找到离vs最近的顶点(k)。
- 526 min = INF;
- 527 for (j = 0; j < G.vexnum; j++)
- 528 {
- 529 if (flag[j]==0 && dist[j]<min)
- 530 {
- 531 min = dist[j];
- 532 k = j;
- 533 }
- 534 }
- 535 // 标记"顶点k"为已经获取到最短路径
- 536 flag[k] = 1;
- 537
- 538 // 修正当前最短路径和前驱顶点
- 539 // 即,当已经"顶点k的最短路径"之后,更新"未获取最短路径的顶点的最短路径和前驱顶点"。
- 540 for (j = 0; j < G.vexnum; j++)
- 541 {
- 542 tmp = (G.matrix[k][j]==INF ? INF : (min + G.matrix[k][j])); // 防止溢出
- 543 if (flag[j] == 0 && (tmp < dist[j]) )
- 544 {
- 545 dist[j] = tmp;
- 546 prev[j] = k;
- 547 }
- 548 }
- 549 }
- 550
- 551 // 打印dijkstra最短路径的结果
- 552 printf("dijkstra(%c): \n", G.vexs[vs]);
- 553 for (i = 0; i < G.vexnum; i++)
- 554 printf(" shortest(%c, %c)=%d\n", G.vexs[vs], G.vexs[i], dist[i]);
- 555 }
- 556
- 557 void main()
- 558 {
- 559 int prev[MAX] = {0};
- 560 int dist[MAX] = {0};
- 561 Graph* pG;
- 562
- 563 // 自定义"图"(输入矩阵队列)
- 564 //pG = create_graph();
- 565 // 采用已有的"图"
- 566 pG = create_example_graph();
- 567
- 568 //print_graph(*pG); // 打印图
- 569 //DFSTraverse(*pG); // 深度优先遍历
- 570 //BFS(*pG); // 广度优先遍历
- 571 //prim(*pG, 0); // prim算法生成最小生成树
- 572 //kruskal(*pG); // kruskal算法生成最小生成树
- 573
- 574 // dijkstra算法获取"第4个顶点"到其它各个顶点的最短距离
- 575 dijkstra(*pG, 3, prev, dist);
- 576 }
- 1
- 2 /**
- 3 * C: Dijkstra算法获取最短路径(邻接表)
- 4 *
7 */- 8
- 9 #include <stdio.h>
- 10 #include <stdlib.h>
- 11 #include <malloc.h>
- 12 #include <string.h>
- 13
- 14 #define MAX 100
- 15 #define INF (~(0x1<<31)) // 最大值(即0X7FFFFFFF)
- 16 #define isLetter(a) ((((a)>='a')&&((a)<='z')) || (((a)>='A')&&((a)<='Z')))
- 17 #define LENGTH(a) (sizeof(a)/sizeof(a[0]))
- 18
- 19 // 邻接表中表对应的链表的顶点
- 20 typedef struct _ENode
- 21 {
- 22 int ivex; // 该边的顶点的位置
- 23 int weight; // 该边的权
- 24 struct _ENode *next_edge; // 指向下一条弧的指针
- 25 }ENode, *PENode;
- 26
- 27 // 邻接表中表的顶点
- 28 typedef struct _VNode
- 29 {
- 30 char data; // 顶点信息
- 31 ENode *first_edge; // 指向第一条依附该顶点的弧
- 32 }VNode;
- 33
- 34 // 邻接表
- 35 typedef struct _LGraph
- 36 {
- 37 int vexnum; // 图的顶点的数目
- 38 int edgnum; // 图的边的数目
- 39 VNode vexs[MAX];
- 40 }LGraph;
- 41
- 42 /*
- 43 * 返回ch在matrix矩阵中的位置
- 44 */
- 45 static int get_position(LGraph G, char ch)
- 46 {
- 47 int i;
- 48 for(i=0; i<G.vexnum; i++)
- 49 if(G.vexs[i].data==ch)
- 50 return i;
- 51 return -1;
- 52 }
- 53
- 54 /*
- 55 * 读取一个输入字符
- 56 */
- 57 static char read_char()
- 58 {
- 59 char ch;
- 60
- 61 do {
- 62 ch = getchar();
- 63 } while(!isLetter(ch));
- 64
- 65 return ch;
- 66 }
- 67
- 68 /*
- 69 * 将node链接到list的末尾
- 70 */
- 71 static void link_last(ENode *list, ENode *node)
- 72 {
- 73 ENode *p = list;
- 74
- 75 while(p->next_edge)
- 76 p = p->next_edge;
- 77 p->next_edge = node;
- 78 }
- 79
- 80 /*
- 81 * 创建邻接表对应的图(自己输入)
- 82 */
- 83 LGraph* create_lgraph()
- 84 {
- 85 char c1, c2;
- 86 int v, e;
- 87 int i, p1, p2;
- 88 int weight;
- 89 ENode *node1, *node2;
- 90 LGraph* pG;
- 91
- 92 // 输入"顶点数"和"边数"
- 93 printf("input vertex number: ");
- 94 scanf("%d", &v);
- 95 printf("input edge number: ");
- 96 scanf("%d", &e);
- 97 if ( v < 1 || e < 1 || (e > (v * (v-1))))
- 98 {
- 99 printf("input error: invalid parameters!\n");
- 100 return NULL;
- 101 }
- 102
- 103 if ((pG=(LGraph*)malloc(sizeof(LGraph))) == NULL )
- 104 return NULL;
- 105 memset(pG, 0, sizeof(LGraph));
- 106
- 107 // 初始化"顶点数"和"边数"
- 108 pG->vexnum = v;
- 109 pG->edgnum = e;
- 110 // 初始化"邻接表"的顶点
- 111 for(i=0; i<pG->vexnum; i++)
- 112 {
- 113 printf("vertex(%d): ", i);
- 114 pG->vexs[i].data = read_char();
- 115 pG->vexs[i].first_edge = NULL;
- 116 }
- 117
- 118 // 初始化"邻接表"的边
- 119 for(i=0; i<pG->edgnum; i++)
- 120 {
- 121 // 读取边的起始顶点,结束顶点,权
- 122 printf("edge(%d): ", i);
- 123 c1 = read_char();
- 124 c2 = read_char();
- 125 scanf("%d", &weight);
- 126
- 127 p1 = get_position(*pG, c1);
- 128 p2 = get_position(*pG, c2);
- 129
- 130 // 初始化node1
- 131 node1 = (ENode*)malloc(sizeof(ENode));
- 132 node1->ivex = p2;
- 133 node1->weight = weight;
- 134 // 将node1链接到"p1所在链表的末尾"
- 135 if(pG->vexs[p1].first_edge == NULL)
- 136 pG->vexs[p1].first_edge = node1;
- 137 else
- 138 link_last(pG->vexs[p1].first_edge, node1);
- 139 // 初始化node2
- 140 node2 = (ENode*)malloc(sizeof(ENode));
- 141 node2->ivex = p1;
- 142 node2->weight = weight;
- 143 // 将node2链接到"p2所在链表的末尾"
- 144 if(pG->vexs[p2].first_edge == NULL)
- 145 pG->vexs[p2].first_edge = node2;
- 146 else
- 147 link_last(pG->vexs[p2].first_edge, node2);
- 148 }
- 149
- 150 return pG;
- 151 }
- 152
- 153 // 边的结构体
- 154 typedef struct _edata
- 155 {
- 156 char start; // 边的起点
- 157 char end; // 边的终点
- 158 int weight; // 边的权重
- 159 }EData;
- 160
- 161 // 顶点
- 162 static char gVexs[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G'};
- 163 // 边
- 164 static EData gEdges[] = {
- 165 // 起点 终点 权
- 166 {'A', 'B', 12},
- 167 {'A', 'F', 16},
- 168 {'A', 'G', 14},
- 169 {'B', 'C', 10},
- 170 {'B', 'F', 7},
- 171 {'C', 'D', 3},
- 172 {'C', 'E', 5},
- 173 {'C', 'F', 6},
- 174 {'D', 'E', 4},
- 175 {'E', 'F', 2},
- 176 {'E', 'G', 8},
- 177 {'F', 'G', 9},
- 178 };
- 179
- 180 /*
- 181 * 创建邻接表对应的图(用已提供的数据)
- 182 */
- 183 LGraph* create_example_lgraph()
- 184 {
- 185 char c1, c2;
- 186 int vlen = LENGTH(gVexs);
- 187 int elen = LENGTH(gEdges);
- 188 int i, p1, p2;
- 189 int weight;
- 190 ENode *node1, *node2;
- 191 LGraph* pG;
- 192
- 193 if ((pG=(LGraph*)malloc(sizeof(LGraph))) == NULL )
- 194 return NULL;
- 195 memset(pG, 0, sizeof(LGraph));
- 196
- 197 // 初始化"顶点数"和"边数"
- 198 pG->vexnum = vlen;
- 199 pG->edgnum = elen;
- 200 // 初始化"邻接表"的顶点
- 201 for(i=0; i<pG->vexnum; i++)
- 202 {
- 203 pG->vexs[i].data = gVexs[i];
- 204 pG->vexs[i].first_edge = NULL;
- 205 }
- 206
- 207 // 初始化"邻接表"的边
- 208 for(i=0; i<pG->edgnum; i++)
- 209 {
- 210 // 读取边的起始顶点,结束顶点,权
- 211 c1 = gEdges[i].start;
- 212 c2 = gEdges[i].end;
- 213 weight = gEdges[i].weight;
- 214
- 215 p1 = get_position(*pG, c1);
- 216 p2 = get_position(*pG, c2);
- 217
- 218 // 初始化node1
- 219 node1 = (ENode*)malloc(sizeof(ENode));
- 220 node1->ivex = p2;
- 221 node1->weight = weight;
- 222 // 将node1链接到"p1所在链表的末尾"
- 223 if(pG->vexs[p1].first_edge == NULL)
- 224 pG->vexs[p1].first_edge = node1;
- 225 else
- 226 link_last(pG->vexs[p1].first_edge, node1);
- 227 // 初始化node2
- 228 node2 = (ENode*)malloc(sizeof(ENode));
- 229 node2->ivex = p1;
- 230 node2->weight = weight;
- 231 // 将node2链接到"p2所在链表的末尾"
- 232 if(pG->vexs[p2].first_edge == NULL)
- 233 pG->vexs[p2].first_edge = node2;
- 234 else
- 235 link_last(pG->vexs[p2].first_edge, node2);
- 236 }
- 237
- 238 return pG;
- 239 }
- 240
- 241 /*
- 242 * 深度优先搜索遍历图的递归实现
- 243 */
- 244 static void DFS(LGraph G, int i, int *visited)
- 245 {
- 246 int w;
- 247 ENode *node;
- 248
- 249 visited[i] = 1;
- 250 printf("%c ", G.vexs[i].data);
- 251 node = G.vexs[i].first_edge;
- 252 while (node != NULL)
- 253 {
- 254 if (!visited[node->ivex])
- 255 DFS(G, node->ivex, visited);
- 256 node = node->next_edge;
- 257 }
- 258 }
- 259
- 260 /*
- 261 * 深度优先搜索遍历图
- 262 */
- 263 void DFSTraverse(LGraph G)
- 264 {
- 265 int i;
- 266 int visited[MAX]; // 顶点访问标记
- 267
- 268 // 初始化所有顶点都没有被访问
- 269 for (i = 0; i < G.vexnum; i++)
- 270 visited[i] = 0;
- 271
- 272 printf("DFS: ");
- 273 for (i = 0; i < G.vexnum; i++)
- 274 {
- 275 if (!visited[i])
- 276 DFS(G, i, visited);
- 277 }
- 278 printf("\n");
- 279 }
- 280
- 281 /*
- 282 * 广度优先搜索(类似于树的层次遍历)
- 283 */
- 284 void BFS(LGraph G)
- 285 {
- 286 int head = 0;
- 287 int rear = 0;
- 288 int queue[MAX]; // 辅组队列
- 289 int visited[MAX]; // 顶点访问标记
- 290 int i, j, k;
- 291 ENode *node;
- 292
- 293 for (i = 0; i < G.vexnum; i++)
- 294 visited[i] = 0;
- 295
- 296 printf("BFS: ");
- 297 for (i = 0; i < G.vexnum; i++)
- 298 {
- 299 if (!visited[i])
- 300 {
- 301 visited[i] = 1;
- 302 printf("%c ", G.vexs[i].data);
- 303 queue[rear++] = i; // 入队列
- 304 }
- 305 while (head != rear)
- 306 {
- 307 j = queue[head++]; // 出队列
- 308 node = G.vexs[j].first_edge;
- 309 while (node != NULL)
- 310 {
- 311 k = node->ivex;
- 312 if (!visited[k])
- 313 {
- 314 visited[k] = 1;
- 315 printf("%c ", G.vexs[k].data);
- 316 queue[rear++] = k;
- 317 }
- 318 node = node->next_edge;
- 319 }
- 320 }
- 321 }
- 322 printf("\n");
- 323 }
- 324
- 325 /*
- 326 * 打印邻接表图
- 327 */
- 328 void print_lgraph(LGraph G)
- 329 {
- 330 int i,j;
- 331 ENode *node;
- 332
- 333 printf("List Graph:\n");
- 334 for (i = 0; i < G.vexnum; i++)
- 335 {
- 336 printf("%d(%c): ", i, G.vexs[i].data);
- 337 node = G.vexs[i].first_edge;
- 338 while (node != NULL)
- 339 {
- 340 printf("%d(%c) ", node->ivex, G.vexs[node->ivex].data);
- 341 node = node->next_edge;
- 342 }
- 343 printf("\n");
- 344 }
- 345 }
- 346
- 347 /*
- 348 * 获取G中边<start, end>的权值;若start和end不是连通的,则返回无穷大。
- 349 */
- 350 int get_weight(LGraph G, int start, int end)
- 351 {
- 352 ENode *node;
- 353
- 354 if (start==end)
- 355 return 0;
- 356
- 357 node = G.vexs[start].first_edge;
- 358 while (node!=NULL)
- 359 {
- 360 if (end==node->ivex)
- 361 return node->weight;
- 362 node = node->next_edge;
- 363 }
- 364
- 365 return INF;
- 366 }
- 367
- 368 /*
- 369 * prim最小生成树
- 370 *
- 371 * 参数说明:
- 372 * G -- 邻接表图
- 373 * start -- 从图中的第start个元素开始,生成最小树
- 374 */
- 375 void prim(LGraph G, int start)
- 376 {
- 377 int min,i,j,k,m,n,tmp,sum;
- 378 int index=0; // prim最小树的索引,即prims数组的索引
- 379 char prims[MAX]; // prim最小树的结果数组
- 380 int weights[MAX]; // 顶点间边的权值
- 381
- 382 // prim最小生成树中第一个数是"图中第start个顶点",因为是从start开始的。
- 383 prims[index++] = G.vexs[start].data;
- 384
- 385 // 初始化"顶点的权值数组",
- 386 // 将每个顶点的权值初始化为"第start个顶点"到"该顶点"的权值。
- 387 for (i = 0; i < G.vexnum; i++ )
- 388 weights[i] = get_weight(G, start, i);
- 389
- 390 for (i = 0; i < G.vexnum; i++)
- 391 {
- 392 // 由于从start开始的,因此不需要再对第start个顶点进行处理。
- 393 if(start == i)
- 394 continue;
- 395
- 396 j = 0;
- 397 k = 0;
- 398 min = INF;
- 399 // 在未被加入到最小生成树的顶点中,找出权值最小的顶点。
- 400 while (j < G.vexnum)
- 401 {
- 402 // 若weights[j]=0,意味着"第j个节点已经被排序过"(或者说已经加入了最小生成树中)。
- 403 if (weights[j] != 0 && weights[j] < min)
- 404 {
- 405 min = weights[j];
- 406 k = j;
- 407 }
- 408 j++;
- 409 }
- 410
- 411 // 经过上面的处理后,在未被加入到最小生成树的顶点中,权值最小的顶点是第k个顶点。
- 412 // 将第k个顶点加入到最小生成树的结果数组中
- 413 prims[index++] = G.vexs[k].data;
- 414 // 将"第k个顶点的权值"标记为0,意味着第k个顶点已经排序过了(或者说已经加入了最小树结果中)。
- 415 weights[k] = 0;
- 416 // 当第k个顶点被加入到最小生成树的结果数组中之后,更新其它顶点的权值。
- 417 for (j = 0 ; j < G.vexnum; j++)
- 418 {
- 419 // 获取第k个顶点到第j个顶点的权值
- 420 tmp = get_weight(G, k, j);
- 421 // 当第j个节点没有被处理,并且需要更新时才被更新。
- 422 if (weights[j] != 0 && tmp < weights[j])
- 423 weights[j] = tmp;
- 424 }
- 425 }
- 426
- 427 // 计算最小生成树的权值
- 428 sum = 0;
- 429 for (i = 1; i < index; i++)
- 430 {
- 431 min = INF;
- 432 // 获取prims[i]在G中的位置
- 433 n = get_position(G, prims[i]);
- 434 // 在vexs[0...i]中,找出到j的权值最小的顶点。
- 435 for (j = 0; j < i; j++)
- 436 {
- 437 m = get_position(G, prims[j]);
- 438 tmp = get_weight(G, m, n);
- 439 if (tmp < min)
- 440 min = tmp;
- 441 }
- 442 sum += min;
- 443 }
- 444 // 打印最小生成树
- 445 printf("PRIM(%c)=%d: ", G.vexs[start].data, sum);
- 446 for (i = 0; i < index; i++)
- 447 printf("%c ", prims[i]);
- 448 printf("\n");
- 449 }
- 450
- 451 /*
- 452 * 获取图中的边
- 453 */
- 454 EData* get_edges(LGraph G)
- 455 {
- 456 int i,j;
- 457 int index=0;
- 458 ENode *node;
- 459 EData *edges;
- 460
- 461 edges = (EData*)malloc(G.edgnum*sizeof(EData));
- 462 for (i=0; i<G.vexnum; i++)
- 463 {
- 464 node = G.vexs[i].first_edge;
- 465 while (node != NULL)
- 466 {
- 467 if (node->ivex > i)
- 468 {
- 469 edges[index].start = G.vexs[i].data; // 起点
- 470 edges[index].end = G.vexs[node->ivex].data; // 终点
- 471 edges[index].weight = node->weight; // 权
- 472 index++;
- 473 }
- 474 node = node->next_edge;
- 475 }
- 476 }
- 477
- 478 return edges;
- 479 }
- 480
- 481 /*
- 482 * 对边按照权值大小进行排序(由小到大)
- 483 */
- 484 void sorted_edges(EData* edges, int elen)
- 485 {
- 486 int i,j;
- 487
- 488 for (i=0; i<elen; i++)
- 489 {
- 490 for (j=i+1; j<elen; j++)
- 491 {
- 492 if (edges[i].weight > edges[j].weight)
- 493 {
- 494 // 交换"第i条边"和"第j条边"
- 495 EData tmp = edges[i];
- 496 edges[i] = edges[j];
- 497 edges[j] = tmp;
- 498 }
- 499 }
- 500 }
- 501 }
- 502
- 503 /*
- 504 * 获取i的终点
- 505 */
- 506 int get_end(int vends[], int i)
- 507 {
- 508 while (vends[i] != 0)
- 509 i = vends[i];
- 510 return i;
- 511 }
- 512
- 513 /*
- 514 * 克鲁斯卡尔(Kruskal)最小生成树
- 515 */
- 516 void kruskal(LGraph G)
- 517 {
- 518 int i,m,n,p1,p2;
- 519 int length;
- 520 int index = 0; // rets数组的索引
- 521 int vends[MAX]={0}; // 用于保存"已有最小生成树"中每个顶点在该最小树中的终点。
- 522 EData rets[MAX]; // 结果数组,保存kruskal最小生成树的边
- 523 EData *edges; // 图对应的所有边
- 524
- 525 // 获取"图中所有的边"
- 526 edges = get_edges(G);
- 527 // 将边按照"权"的大小进行排序(从小到大)
- 528 sorted_edges(edges, G.edgnum);
- 529
- 530 for (i=0; i<G.edgnum; i++)
- 531 {
- 532 p1 = get_position(G, edges[i].start); // 获取第i条边的"起点"的序号
- 533 p2 = get_position(G, edges[i].end); // 获取第i条边的"终点"的序号
- 534
- 535 m = get_end(vends, p1); // 获取p1在"已有的最小生成树"中的终点
- 536 n = get_end(vends, p2); // 获取p2在"已有的最小生成树"中的终点
- 537 // 如果m!=n,意味着"边i"与"已经添加到最小生成树中的顶点"没有形成环路
- 538 if (m != n)
- 539 {
- 540 vends[m] = n; // 设置m在"已有的最小生成树"中的终点为n
- 541 rets[index++] = edges[i]; // 保存结果
- 542 }
- 543 }
- 544 free(edges);
- 545
- 546 // 统计并打印"kruskal最小生成树"的信息
- 547 length = 0;
- 548 for (i = 0; i < index; i++)
- 549 length += rets[i].weight;
- 550 printf("Kruskal=%d: ", length);
- 551 for (i = 0; i < index; i++)
- 552 printf("(%c,%c) ", rets[i].start, rets[i].end);
- 553 printf("\n");
- 554 }
- 555
- 556 /*
- 557 * Dijkstra最短路径。
- 558 * 即,统计图(G)中"顶点vs"到其它各个顶点的最短路径。
- 559 *
- 560 * 参数说明:
- 561 * G -- 图
- 562 * vs -- 起始顶点(start vertex)。即计算"顶点vs"到其它顶点的最短路径。
- 563 * prev -- 前驱顶点数组。即,prev[i]的值是"顶点vs"到"顶点i"的最短路径所经历的全部顶点中,位于"顶点i"之前的那个顶点。
- 564 * dist -- 长度数组。即,dist[i]是"顶点vs"到"顶点i"的最短路径的长度。
- 565 */
- 566 void dijkstra(LGraph G, int vs, int prev[], int dist[])
- 567 {
- 568 int i,j,k;
- 569 int min;
- 570 int tmp;
- 571 int flag[MAX]; // flag[i]=1表示"顶点vs"到"顶点i"的最短路径已成功获取。
- 572
- 573 // 初始化
- 574 for (i = 0; i < G.vexnum; i++)
- 575 {
- 576 flag[i] = 0; // 顶点i的最短路径还没获取到。
- 577 prev[i] = 0; // 顶点i的前驱顶点为0。
- 578 dist[i] = get_weight(G, vs, i); // 顶点i的最短路径为"顶点vs"到"顶点i"的权。
- 579 }
- 580
- 581 // 对"顶点vs"自身进行初始化
- 582 flag[vs] = 1;
- 583 dist[vs] = 0;
- 584
- 585 // 遍历G.vexnum-1次;每次找出一个顶点的最短路径。
- 586 for (i = 1; i < G.vexnum; i++)
- 587 {
- 588 // 寻找当前最小的路径;
- 589 // 即,在未获取最短路径的顶点中,找到离vs最近的顶点(k)。
- 590 min = INF;
- 591 for (j = 0; j < G.vexnum; j++)
- 592 {
- 593 if (flag[j]==0 && dist[j]<min)
- 594 {
- 595 min = dist[j];
- 596 k = j;
- 597 }
- 598 }
- 599 // 标记"顶点k"为已经获取到最短路径
- 600 flag[k] = 1;
- 601
- 602 // 修正当前最短路径和前驱顶点
- 603 // 即,当已经"顶点k的最短路径"之后,更新"未获取最短路径的顶点的最短路径和前驱顶点"。
- 604 for (j = 0; j < G.vexnum; j++)
- 605 {
- 606 tmp = get_weight(G, k, j);
- 607 tmp = (tmp==INF ? INF : (min + tmp)); // 防止溢出
- 608 if (flag[j] == 0 && (tmp < dist[j]) )
- 609 {
- 610 dist[j] = tmp;
- 611 prev[j] = k;
- 612 }
- 613 }
- 614 }
- 615
- 616 // 打印dijkstra最短路径的结果
- 617 printf("dijkstra(%c): \n", G.vexs[vs].data);
- 618 for (i = 0; i < G.vexnum; i++)
- 619 printf(" shortest(%c, %c)=%d\n", G.vexs[vs].data, G.vexs[i].data, dist[i]);
- 620 }
- 621
- 622 void main()
- 623 {
- 624 int prev[MAX] = {0};
- 625 int dist[MAX] = {0};
- 626 LGraph* pG;
- 627
- 628 // 自定义"图"(自己输入数据)
- 629 //pG = create_lgraph();
- 630 // 采用已有的"图"
- 631 pG = create_example_lgraph();
- 632
- 633 //print_lgraph(*pG); // 打印图
- 634 //DFSTraverse(*pG); // 深度优先遍历
- 635 //BFS(*pG); // 广度优先遍历
- 636 //prim(*pG, 0); // prim算法生成最小生成树
- 637 //kruskal(*pG); // kruskal算法生成最小生成树
- 638
- 639 // dijkstra算法获取"第4个顶点"到其它各个顶点的最短距离
- 640 dijkstra(*pG, 3, prev, dist);
- 641 }
图的全部实现(邻接矩阵 邻接表 BFS DFS 最小生成树 最短路径等)的更多相关文章
- 数据结构学习笔记05图 (邻接矩阵 邻接表-->BFS DFS、最短路径)
数据结构之图 图(Graph) 包含 一组顶点:通常用V (Vertex) 表示顶点集合 一组边:通常用E (Edge) 表示边的集合 边是顶点对:(v, w) ∈E ,其中v, w ∈ V 有向边& ...
- PAT1013. Battle Over Cities(邻接矩阵、邻接表分别dfs)
//采用不同的图存储结构结构邻接矩阵.邻接表分别dfs,我想我是寂寞了吧,应该试试并查集,看见可以用并查集的就用dfs,bfs代替......怕了并查集了 //邻接矩阵dfs #include< ...
- 网络流三大算法【邻接矩阵+邻接表】POJ1273
网络流的基本概念跟算法原理我是在以下两篇博客里看懂的,写的非常好. http://www.cnblogs.com/ZJUT-jiangnan/p/3632525.html http://www.cnb ...
- 【数据结构】【图文】【oj习题】 图的拓扑排序(邻接表)
拓扑排序: 按照有向图给出的次序关系,将图中顶点排成一个线性序列,对于有向图中没有限定次序关系的顶点,则可以人为加上任意的次序关系,由此所得顶点的线性序列称之为拓扑有序序列.显然对于有回路的有向图得不 ...
- 图的基本遍历算法的实现(BFS & DFS)复习
#include <stdio.h> #define INF 32767 typedef struct MGraph{ ]; ][]; int ver_num, edge_num; }MG ...
- 第6章 图的学习总结(邻接矩阵&邻接表)
我觉得图这一章的学习内容更有难度,其实图可以说是树结构更为普通的表现形式,它的每个元素都可以与多个元素之间相关联,所以结构比树更复杂,然而越复杂的数据结构在现实中用途就越大了,功能与用途密切联系,所以 ...
- 图结构练习——BFS——从起始点到目标点的最短步数(邻接表+BFS)
图练习-BFS-从起点到目标点的最短步数 Time Limit: 1000ms Memory limit: 65536K 有疑问?点这里^_^ 题目描写叙述 在古老的魔兽传说中.有两个军团,一个 ...
- 图的基本操作(基于邻接表):图的构造,深搜(DFS),广搜(BFS)
#include <iostream> #include <string> #include <queue> using namespace std; //表结点 ...
- ACM/ICPC 之 数据结构-邻接表+BFS(TSH OJ-无线广播Broadcast)
这道题中若能够构成互不干扰的区域,其构成的图其实就是汉密尔顿路(Hamilton road),因此如果能够观察出来可以直接转化为汉密尔顿路的存在性证明,即便不能观察,我相信ACMer也能转化为BFS问 ...
随机推荐
- Dledger的是如何实现主从自动切换的
前言 hello小伙伴们,今天王子又来继续和大家聊RocketMQ了,之前的文章我们一直说Broker的主从切换是可以基于Dledger实现自动切换的,那么小伙伴们是不是很好奇它究竟是如何实现的呢?今 ...
- React代码开发规范
前言 一般在团队开发中每个人的代码习惯都不太一样,这样就会导致代码风格不一致,以致于维护和修改bug的时候看别人的代码成为一种痛苦...这种情况尤其在前端开发中尤为明显.因为关于前端的开发规范貌似也没 ...
- 你在开发过程中使用Git Rebase还是Git Merge?
摘要:在git里面经常的一个争论是到底用rebase还是用merge? 1. 痛苦吗?代码历史中的迷失羔羊 我们先来看一个真实的代码提交历史图形化截图: 图片源自 https://storage.kr ...
- vs工程生成dll文件及其调用方法
转载:https://blog.csdn.net/weixin_44536482/article/details/91519413 vs工程生成dll文件及其调用方法 ...
- AD(Altium Designer)PCB布线中的“格式刷”,助力快速布局布线
摘要:在AD(Altium Designer)进行电路板布线时,孔丙火(微信公众号:孔丙火)经常会碰到电路中有相同功能的模块,比如2路相同的RS485通信电路.多路相同继电器输出电路.多路相同的输入电 ...
- shell-的变量-局部变量
1. 定义本地变量 本地变量在用户当前的shell生产期的脚本中使用.例如,本地变量OLDBOY取值为ett098,这个值只在用户当前shell生存期中有意义.如果在shell中启动另一个进程或退出, ...
- .NET Standard 类库的使用技巧
系列目录 [已更新最新开发文章,点击查看详细] 在前一篇博客<.NET Standard中配置TargetFrameworks输出多版本类库>中详细介绍了如何创建.配置.条件编译. ...
- Cypress系列(63)- 使用 Custom Commands
如果想从头学起Cypress,可以看下面的系列文章哦 https://www.cnblogs.com/poloyy/category/1768839.html Custom Commands 自定义命 ...
- 网站搭建-云服务器ECS的使用
1. 查看购买的云服务器实例,重置密码 2. 查找IP进行查看,此时网页时不存在的,开始配置: 3. 登录putty或其他终端,进行网页搭建,先按教程走一遍,然后再做个性化处理: #安装Apache ...
- 2016年 实验五 Internet与网络工具的使用
实验五 Internet与网络工具的使用 [实验目的] 本实验目的在于掌握 Telnet.FTP.Email的工作机理,并会在 Internet 上熟练使用相关工具软件. [实验条件] ⑴.个人计算机 ...