图的全部实现(邻接矩阵 邻接表 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问 ...
随机推荐
- Apache Flink on K8s:四种运行模式,我该选择哪种?
1. 前言 Apache Flink 是一个分布式流处理引擎,它提供了丰富且易用的API来处理有状态的流处理应用,并且在支持容错的前提下,高效.大规模的运行此类应用.通过支持事件时间(event-ti ...
- BeautifulSoup解析页面
beautiful soup是一个解析包,专门用来解析html语法的,lxml是一个解析器,用来分析以及定位内容的 .是class #是id import requests from bs4 impo ...
- Git裸仓库的分支(Active Branch)切换
Git裸仓库的Active Branch切换方法 在服务器上通过init --bare创建了一个裸仓库作为远程仓库使用,并且存在三个分支(master/kid/develop),但在使用中发现代码虽然 ...
- 关于bat/cmd中转义符的使用
今天笔者在cmd中准备使用echo 输出<https://www.cnblogs.com/5201351> 发现直接就报错:命令语法不正确. 然后就想到可能是<和>在cmd中有 ...
- 提效工具-python解析xmind文件及xmind用例统计
现状 每个公司都有一个维护测试case的系统,有自研的也有买的,比如QC, 禅道等等,QA往往习惯使用xmind等思维导图工具来编写测试用例,因为思路清晰,编写方便,那么这就有一个问题,大多公司要求所 ...
- 渗透测试方法论(qf总结)
渗透测试(penetration testing , pentest)是实施安全评估(即审计)的具体手段.方法论是在指定.实施信息安全审计方案时,需要遵循的规则.惯例和过程.人们在评估网路.应用.系统 ...
- ARM架构下的Docker环境,OpenJDK官方没有8版本镜像,如何完美解决?
为什么需要ARM架构下的OpenJDK8的Docker镜像? 对现有的Java应用,之前一直运行在x86处理器环境下,编译和运行都是JDK8,如今在树莓派的Docker环境运行(或者其他ARM架构电脑 ...
- Oracle12c 使用总结
/*创建临时表空间 */create temporary tablespace BBB tempfile 'D:\APP\oracle\oradata\orcl\BBB.dbf' size 500m ...
- 理解RESTful:理论与最佳实践
什么是 REST 什么是 RESTful Richardson 成熟度模型 RESTful API 设计最佳实践 补充:HTTP 状态码及说明 什么是 REST REST 一词,是由 HTTP 协议的 ...
- mysql-7-join
#进阶7:连接查询 /* 多表连接:查询的字段来自多个表 按功能分类: 内连接: 等值连接 非等值连接 自连接 外连接: 左外连接 右外连接 全外连接 交叉连接 */ USE girls; SELEC ...