圖(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 }