图的存储结构与操作--C语言实现
图(graph)是一种比树结构还要复杂的数据结构,它的术语,存储方式,遍历方式,用途都比较广,所以如果想要一次性完成所有的代码,那代码会非常长。所以,我将分两次来完成图的代码。这一次,我会完成图的五种存储结构的创建(邻接矩阵存储,邻接表存储,十字链表存储,邻接多重表存储,边集数组存储),两种遍历方式(深度优先遍历,广度优先遍历)。与树结构一样,图结构的遍历也需要借助队列来协助实现。
1 #include<stdio.h>
2 #include<malloc.h>
3 typedef char VertexType; //顶点类型
4 typedef int EdgeType; //权值类型
5 #define MAXVEX 100 //最大顶点数
6 #define INFINITY 65535 //无限大
7 #define TURE 1
8 #define FALSE 0
9 typedef int Boolean;
10 Boolean visited[MAXVEX]; //记录访问过的结点数组
11
12 #define MAXSIZE 100 //队列最大空间
13 typedef int QElemType; //队列中元素类型
14 typedef int Status; //返回值类型
15 #define OK 1 //操作成功
16 #define ERROR 0 //操作失败
17
18 typedef struct //队列结点结构
19 {
20 QElemType date[MAXSIZE]; //结点元素
21 int front; //队头
22 int rear; //队尾
23 }SqQueue;
24
25 /*队列的初始化*/
26 Status InitQue(SqQueue *Q)
27 {
28 Q->front = 0; //队头指向0
29 Q->rear = 0; //队尾指向0
30 return OK;
31 }
32
33 /*队列的入队操作*/
34 Status EnQueue(SqQueue *Q, QElemType e)
35 {
36 if((Q->rear + 1) % MAXSIZE == Q->front) //判断队列是否已满
37 return ERROR;
38 Q->date[Q->rear] = e; //队尾赋值为e
39 Q->rear = (Q->rear + 1) % MAXSIZE; //队尾后移
40 return OK;
41 }
42
43 /*队列的出队操作*/
44 Status DeQueue(SqQueue *Q, QElemType *e)
45 {
46 if(Q->front == Q->rear) //判断队列是否为空
47 return ERROR;
48 *e = Q->date[Q->front]; //将队头元素取出
49 Q->front = (Q->front + 1) % MAXSIZE; //队头后移
50 return OK;
51 }
52
53 /*获取队列的长度*/
54 int LengthQue(SqQueue Q)
55 {
56 return (Q.rear - Q.front + MAXSIZE) % MAXSIZE;
57 }
58
59 //邻接矩阵结构体
60 typedef struct
61 {
62 VertexType adList[MAXVEX]; //声明顶点数组
63 EdgeType arc[MAXVEX][MAXVEX]; //声明权值数组
64 int numVerexes,numEdges; //顶点数,边数
65 }Graph;
66
67 //邻接表结构体
68 typedef struct EdgeNode //边表结构体
69 {
70 int adjSub; //存储边表的下标
71 // EdgeType weight; //权重
72 struct EdgeNode *next; //指向下一个相邻顶点的指针
73 }EdgeNode;
74
75 typedef struct VertexNode //顶点结构体
76 {
77 VertexType date; //顶点内容
78 EdgeNode *firstEdge; //指向顶点第一个邻接点
79 }GraphList[MAXVEX];
80
81 typedef struct //邻接表结构体
82 {
83 GraphList graphList; //顶点数组
84 int numVerexes, numEdges; //顶点数,边数
85 }AjaGraph;
86
87 //十字链表结构体
88 typedef struct Orthogonal //边表结构体
89 {
90 int tailVex; //当前顶点下标
91 int headVex; //弧尾顶点下标
92 struct Orthogonal *tailLink; //指向入度弧的弧尾
93 struct Orthogonal *headLink; //指向出度弧的弧头
94 }Orthogonal;
95
96 typedef struct //顶点结构体
97 {
98 VertexType date; //顶点内容
99 Orthogonal *firstin; //指向第一个弧头为自己的点
100 Orthogonal *firstout; //指向第一个弧尾为自己的点
101 }Orthogonal_Node[MAXVEX];
102
103 typedef struct
104 {
105 Orthogonal_Node orth_Node; //声明结点数组
106 int numVertexes,numEdges; //顶点数量,边数量
107 }OrthGraph; //十字链表图结构
108
109 //边集数组结构体
110 typedef struct //边结构体
111 {
112 int iVex; //顶点位置
113 int jVex; //顶点位置
114 EdgeType weight; //权重
115 }EdgeArray[MAXVEX];
116
117 typedef struct //图结构体
118 {
119 VertexType VexterList[MAXVEX]; //顶点数组
120 EdgeArray EdgeList; //边数组
121 int numVexteres, numEdges; //顶点数量,边数量
122 }EdgeListArray; //边集数组图结构
123
124 //邻接多重表
125 typedef struct EdgeList_multiple //边表结点
126 {
127 int iVex; //当前顶点下标
128 int jVex; //终点下表
129 struct EdgeList_multiple *iLink; //指向与顶点i有相同起点的结点
130 struct EdgeList_multiple *jLink; //指向与顶点j有相同终点的结点
131 }EdgeList_multiple;
132
133 typedef struct
134 {
135 VertexType date; //顶点数据
136 EdgeList_multiple *firstEdge; //指向顶点的第一个邻接点
137 }Vexs;
138
139 typedef struct
140 {
141 Vexs vexs[MAXVEX]; //建立顶点数组
142 int numVexes; //顶点数量
143 int numEdges; //边的数量
144 }MultipleGraph;
145
146 //邻接多重表创建图结构
147 int CreatGraphMultiple(MultipleGraph *G)
148 {
149 int i,j,k;
150 printf("请输入顶点数i与边的数量j:");
151 scanf("%d,%d",&G->numVexes,&G->numEdges);
152 EdgeList_multiple *em;
153 getchar();
154 for(i = 0; i < G->numVexes; i++)
155 {
156 printf("请输入第%d个顶点:",i);
157 scanf("%c", &G->vexs[i].date);
158 getchar();
159 }
160 for(k = 0; k < G->numEdges; k++)
161 {
162 printf("请输入边的起点i与终点j的下标:");
163 scanf("%d,%d",&i,&j);
164 em = (EdgeList_multiple *)malloc(sizeof(EdgeList_multiple)); //创建新结点空间
165 em->iVex = i;
166 em->jVex = j;
167 em->iLink = G->vexs[i].firstEdge;
168 G->vexs[i].firstEdge = em;
169 em->jLink = G->vexs[j].firstEdge;
170 G->vexs[j].firstEdge = em;
171 }
172 return 1;
173 }
174
175 //邻接矩阵创建图结构
176 int CreatGraph(Graph *G)
177 {
178 int i,j,k,w;
179 printf("请输入结点数i,边数j:");
180 scanf("%d,%d",&G->numVerexes,&G->numEdges); //写入顶点数和边数
181 for(i = 0; i < G->numVerexes; i++) //初始化顶点数组
182 {
183 printf("请输入第%d个顶点:",i);
184 scanf("%c",&G->adList[i]);
185 getchar();
186 }
187 for(i = 0; i < G->numVerexes; i++) //初始化权值矩阵
188 for(j = 0; j < G->numVerexes; j++)
189 G->arc[i][j] = INFINITY;
190 for(k = 0; k<G->numEdges; k++) //写入权值
191 {
192 printf("请输入需要添加权值的下标i和下标j,及其权值w:");
193 scanf("%d,%d,%d", &i, &j, &w);
194 G->arc[i][j] = w;
195 G->arc[j][i] = G->arc[i][j]; //无向图的对称性
196 }
197 return 1;
198 }
199
200 //邻接表创建图结构
201 int CreatAjaGraph(AjaGraph *G)
202 {
203 int i, j, k;
204 char a;
205 EdgeNode *e; //声明边表新结点
206 printf("请输入顶点i和边数j:");
207 scanf("%d, %d", &G->numVerexes, &G->numEdges); //写入顶点数与边数
208 getchar();
209 for(i = 0; i < G->numVerexes; i++) //初始化顶点数组
210 {
211 printf("请输入第%d个结点:",i);
212 scanf("%c",&G->graphList[i].date);
213 getchar();
214 printf("%c\n", a);
215 G->graphList[i].firstEdge = NULL; //顶点数组的指针域指向空
216 }
217 for(k = 0; k < G->numEdges; k++) //构建边表
218 {
219 printf("请输入邻接点Vi与Vj的下标:");
220 scanf("%d,%d", &i, &j);
221 e = (EdgeNode *)malloc(sizeof(EdgeNode)); //创建新结点空间
222 e->adjSub = j; //新结点的数据域为j
223 e->next = G->graphList[i].firstEdge; //新结点指针域指向顶点指针域
224 G->graphList[i].firstEdge = e; //顶点指针域指向新结点
225
226 e = (EdgeNode *)malloc(sizeof(EdgeNode)); //因为是无向图
227 e->adjSub = i; //同时为i操作
228 e->next = G->graphList[j].firstEdge;
229 G->graphList[j].firstEdge = e;
230 }
231
232 return 1;
233 }
234
235 //十字链表结构创建图结构
236 int CreatOrthGraph(OrthGraph *G)
237 {
238 int i,j,k;
239 Orthogonal *e;
240 printf("请输入顶点数量i和边数量j:");
241 scanf("%d,%d", &G->numVertexes, &G->numEdges); //写入顶点数和边数
242 for(i = 0; i < G->numVertexes; i++) //对顶点数组初始化
243 {
244 printf("请输入第%d个结点:",i);
245 scanf("%c",&G->orth_Node[i].date); //输入顶点内容
246 getchar();
247 G->orth_Node[i].firstin = NULL; //将入边表指向空
248 G->orth_Node[i].firstout = NULL; //将出边表指向空
249 }
250 for(k = 0; k < G->numEdges; k++) //构建边表
251 {
252 printf("请输入起点i与终点j的下标:");
253 scanf("%d,%d", &i, &j);
254 e = (Orthogonal *)malloc(sizeof(Orthogonal)); //创建新结点空间
255 e->tailVex = i; //当前结点等于i
256 e->headVex = j; //弧尾等于j
257 e->tailLink = G->orth_Node[i].firstout; //入度指针等于顶点入度指针
258 G->orth_Node[i].firstout = e; //顶点位置i的firstout指向e
259 e->headLink = G->orth_Node[j].firstin; //出度指针等于顶点出度指针
260 G->orth_Node[j].firstin = e; //顶点位置j的firstout指向e
261 }
262 return 1;
263 }
264
265 //边集数组创建图结构
266 int CreatGraph(EdgeListArray *G)
267 {
268 int i, j, w;
269 printf("请输入顶点数量i与边的数量j:");
270 scanf("%d,%d", &G->numVexteres, &G->numEdges); //写入顶点数量与边数量
271 for(i = 0; i < G->numVexteres; i++) //构建顶点数组
272 {
273 printf("请输入第%d个结点:",i);
274 scanf("%c",&G->VexterList[i]);
275 }
276 for(i = 0; i < G->numEdges; i++) //构建边数组
277 {
278 printf("请输入顶点i与顶点j及其权重w:");
279 scanf("%d,%d", &i, &j, &w);
280 G->EdgeList[i].iVex = i; //这里的i只是边数组的下标,与顶点数组无关
281 G->EdgeList[i].jVex = j;
282 G->EdgeList[i].weight = w;
283 }
284 return 1;
285 }
286
287 //遍历邻接矩阵结构
288 void PrintfGraph(Graph G)
289 {
290 int i,j;
291 for(i = 0; i < G.numVerexes; i++)
292 for(j = 0; j < G.numVerexes; j++)
293 if(G.arc[i][j] != INFINITY && i < j)
294 printf("顶点: %d , %d, 权重: %d\n", i, j, G.arc[i][j]);
295 }
296
297 //邻接表深度优先遍历
298 void PrintDeepthAjaGraph(AjaGraph G, int i) //递归函数
299 {
300 visited[i] = TURE; //将此顶点记为访问过
301 printf("%c\n", G.graphList[i].date); //打印当前顶点
302 EdgeNode *ag; //创建顶点指针
303 ag = G.graphList[i].firstEdge; //将此指针赋值为当前顶点的边表第一个结点
304 while(ag) //只要ag不为空
305 {
306 if(!visited[ag->adjSub]) //如果当前边表第一个结点不为空
307 PrintDeepthAjaGraph(G, ag->adjSub); //递归
308 ag = ag->next; //否则ag赋值为ag的下一临结点
309 }
310 }
311
312 void Depth_first(AjaGraph G) //深度优先遍历函数
313 {
314 int j;
315 for(j = 0; j < G.numVerexes; j++) //初始化记录数组
316 visited[j] = FALSE;
317 for(j = 0; j < G.numVerexes; j++) //遍历顶点数组中的每一个顶点
318 {
319 // printf("当前结点是:%d, 其是否遍历过 %d\n", j, visited[j]);
320 if(!visited[j]) //如果当前结点没被访问过
321 PrintDeepthAjaGraph(G, j); //调用递归函数
322 }
323 }
324
325 //邻接表广度优先搜索
326 void BFs(AjaGraph G)
327 {
328 int j;
329 SqQueue Q; //创建队Q
330 InitQue(&Q); //初始化队列
331 for(j = 0; j < G.numVerexes; j++) //初始化记录数组
332 visited[j] = FALSE;
333 EdgeNode *ag; //创建边表指针
334 visited[0] = TURE; //将第一个顶点记为访问过
335 printf("%c\n", G.graphList[0].date); //打印第一个顶点
336 EnQueue(&Q, 0); //将第一个顶点入队
337 while(Q.front != Q.rear) //只要队列不为空
338 {
339 DeQueue(&Q, &j); //将当前顶点出队
340 ag = G.graphList[j].firstEdge; //ag赋值为当前结点的第一个边表结点
341 while(ag && !visited[ag->adjSub]) //ag不为空且ag未被访问过
342 {
343 visited[ag->adjSub] = TURE; //将ag记为访问过
344 printf("%c\n", G.graphList[ag->adjSub].date); //打印ag
345 EnQueue(&Q, ag->adjSub); //将ag入队
346 ag = ag->next; //ag赋值为ag的下一邻接表结点
347 }
348 }
349 }
350
351 void main()
352 {
353 Graph G1;
354 AjaGraph G2;
355 OrthGraph G3;
356 MultipleGraph G4;
357 EdgeListArray G5;
358 while(true)
359 {
360 int flag = 0;
361 printf("请选择对图的操作:\n");
362 printf("1.邻接矩阵存储创建\n");
363 printf("2.邻接表存储创建\n");
364 printf("3.十字链表存储创建\n");
365 printf("4.邻接多重表创建\n");
366 printf("5.边集数组创建\n");
367 printf("6.遍历邻接矩阵图结构\n");
368 printf("7.邻接表深度优先遍历\n");
369 printf("8.遍历线索化二叉树\n");
370 printf("9.退出\n");
371 int a;
372 scanf("%d", &a);
373 switch(a)
374 {
375 case 1:
376 flag = 0;
377 flag = CreatGraph(&G1);
378 if(flag)
379 printf("创建成功\n");
380 else
381 printf("创建失败\n");
382 break;
383 case 2:
384 flag = 0;
385 flag = CreatAjaGraph(&G2);
386 if(flag)
387 printf("创建成功\n");
388 else
389 printf("创建失败\n");
390 break;
391 case 3:
392 flag = 0;
393 flag = CreatOrthGraph(&G3);
394 if(flag)
395 printf("创建成功\n");
396 else
397 printf("创建失败\n");
398 break;
399 case 4:
400 flag = 0;
401 flag = CreatGraphMultiple(&G4);
402 if(flag)
403 printf("创建成功\n");
404 else
405 printf("创建失败\n");
406 break;
407 case 5:
408 flag = 0;
409 CreatGraph(&G5);
410 if(flag)
411 printf("创建成功\n");
412 else
413 printf("创建失败\n");
414 break;
415 case 6:
416 PrintfGraph(G1);
417 break;
418 case 7:
419 Depth_first(G2);
420 break;
421 case 8:
422 BFs(G2);
423 break;
424 case 9:
425 return;
426 default:
427 printf("选择错误\n");
428 break;
429 }
430 }
431 }