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 最小生成树 最短路径等)的更多相关文章

  1. 数据结构学习笔记05图 (邻接矩阵 邻接表-->BFS DFS、最短路径)

    数据结构之图 图(Graph) 包含 一组顶点:通常用V (Vertex) 表示顶点集合 一组边:通常用E (Edge) 表示边的集合 边是顶点对:(v, w) ∈E ,其中v, w ∈ V 有向边& ...

  2. PAT1013. Battle Over Cities(邻接矩阵、邻接表分别dfs)

    //采用不同的图存储结构结构邻接矩阵.邻接表分别dfs,我想我是寂寞了吧,应该试试并查集,看见可以用并查集的就用dfs,bfs代替......怕了并查集了 //邻接矩阵dfs #include< ...

  3. 网络流三大算法【邻接矩阵+邻接表】POJ1273

    网络流的基本概念跟算法原理我是在以下两篇博客里看懂的,写的非常好. http://www.cnblogs.com/ZJUT-jiangnan/p/3632525.html http://www.cnb ...

  4. 【数据结构】【图文】【oj习题】 图的拓扑排序(邻接表)

    拓扑排序: 按照有向图给出的次序关系,将图中顶点排成一个线性序列,对于有向图中没有限定次序关系的顶点,则可以人为加上任意的次序关系,由此所得顶点的线性序列称之为拓扑有序序列.显然对于有回路的有向图得不 ...

  5. 图的基本遍历算法的实现(BFS & DFS)复习

    #include <stdio.h> #define INF 32767 typedef struct MGraph{ ]; ][]; int ver_num, edge_num; }MG ...

  6. 第6章 图的学习总结(邻接矩阵&邻接表)

    我觉得图这一章的学习内容更有难度,其实图可以说是树结构更为普通的表现形式,它的每个元素都可以与多个元素之间相关联,所以结构比树更复杂,然而越复杂的数据结构在现实中用途就越大了,功能与用途密切联系,所以 ...

  7. 图结构练习——BFS——从起始点到目标点的最短步数(邻接表+BFS)

    图练习-BFS-从起点到目标点的最短步数 Time Limit: 1000ms   Memory limit: 65536K  有疑问?点这里^_^ 题目描写叙述 在古老的魔兽传说中.有两个军团,一个 ...

  8. 图的基本操作(基于邻接表):图的构造,深搜(DFS),广搜(BFS)

    #include <iostream> #include <string> #include <queue> using namespace std; //表结点 ...

  9. ACM/ICPC 之 数据结构-邻接表+BFS(TSH OJ-无线广播Broadcast)

    这道题中若能够构成互不干扰的区域,其构成的图其实就是汉密尔顿路(Hamilton road),因此如果能够观察出来可以直接转化为汉密尔顿路的存在性证明,即便不能观察,我相信ACMer也能转化为BFS问 ...

随机推荐

  1. leetcode1546题解【前缀和+贪心】

    leetcode1546.和为目标值的最大数目不重叠非空子数组数目 题目链接 算法 前缀和+贪心 时间复杂度O(n). 1.对nums数组求前缀和: 2.在求前缀和过程中将前缀和sum插入到set集合 ...

  2. Redis中有序列表(ZSet)相关命令

    redis语序集合和集合set是一样内部value为string类型的集合,有序不允许重复元素 但是,zset的每个元素有一个double类型的分数(score).redis正是靠这个分数对元素从小到 ...

  3. Spring Boot学习(一)初识Spring Boot

    Spring Boot 概述 Spring Boot 是所有基于 Spring 开发的项目的起点.Spring Boot 的设计是为了让你尽可能快的跑起来 Spring 应用程序并且尽可能减少你的配置 ...

  4. python ---倒酒!!

    #!/usr/bin/env python3# -*- coding: utf-8 -*-import numbersimport numpyimport math'''三个容器分别为12升.8升.5 ...

  5. python-igraph

    linux安装python-igraph: $ sudo apt-get install -y libigraph0-dev $ pip install python-igraph ------for ...

  6. Centos-服务管理-systemctl

    systemctl命令属于systemd软件包,这个软件包不仅可以完成系统的初始化工作,还能对系统和服务进行管理 在centos7中,服务单元取代启动脚本,服务单元以.service为文件扩展名,配置 ...

  7. 手写“SpringBoot”近况:IoC模块已经完成

    jsoncat:https://github.com/Snailclimb/jsoncat (About 仿 Spring Boot 但不同于 Spring Boot 的一个轻量级的 HTTP 框架) ...

  8. 005 01 Android 零基础入门 01 Java基础语法 01 Java初识 05 Eclipse简介

    005 01 Android 零基础入门 01 Java基础语法 01 Java初识 05 Eclipse简介 Eclipse是一款集成开发工具--IDE. 集成开发环境(IDE,Integrated ...

  9. Java知识系统回顾整理01基础01第一个程序07Eclipse使用----找不到类如何解决?

    一.现象 有时候会碰到如图所示的问题,分明有Hello这个类,并且也有主方法,可是运行就会出现找不到或者无法加载类Hello,或者Class Not Found 异常. 出现这个状况,有多种原因造成, ...

  10. 【学习笔记/题解】分层图/[JLOI2011]飞行路线

    题目戳我 \(\text{Solution:}\) 关于分层图: 一般用于处理:给你\(k\)次机会对边权进行修改的最短路问题. 算法流程: 建立出\(k\)层图,对应进行\(k\)次操作后的局面. ...