圖的存儲結構相對於線性表和樹來說更為復雜,因為圖中的頂點具有相對概念,沒有固定的位置。那我們怎么存儲圖的數據結構呢?我們知道,圖是由(V, E)來表示的,對於無向圖來說,其中 V = (v0, v1, ... , vn),E = { (vi,vj) (0 <= i, j <= n且i 不等於j)},對於有向圖,E = { < vi,vj > (0 <= i, j <= n且i 不等於j)}。V是頂點的集合,E是邊的集合。所以我們只要把頂點和邊的集合儲存起來,那么該圖的所有數據就能夠存儲起來了。
本文只介紹兩種比較常見和重要的圖的存儲結構:鄰接矩陣和鄰接表。
鄰接矩陣,顧名思義,是一個矩陣,一個存儲着邊的信息的矩陣,而頂點則用矩陣的下標表示。對於一個鄰接矩陣M,如果M(i,j)=1,則說明頂點i和頂點j之間存在一條邊,對於無向圖來說,M (j ,i) = M (i, j),所以其鄰接矩陣是一個對稱矩陣;對於有向圖來說,則未必是一個對稱矩陣。鄰接矩陣的對角線元素都為0。下圖是一個無向圖和對應的臨街矩陣:

圖1:無向圖

圖2:鄰接矩陣
需要注意的是,當邊上有權值的時候,稱之為網圖,則鄰接矩陣中的元素不再僅是0和1了,鄰接矩陣M中的元素定義為:
。
以下用C語言創建一個無向圖的鄰接矩陣:
頭文件是:GraphStruct.h
1 /*GraphStruct.h 2 * 圖的鄰接矩陣存儲方式,結構由頂點數量、邊數量、頂點集合和邊集合組成。 3 * 其中頂點集合一維數組,根據頂點的數量動態分配數組大小。 4 * 邊集合是二維數組,根據頂點的數量來動態分配數組大小,對於無向圖來說,該鄰接矩陣是對稱矩陣。 5 * 鄰接矩陣比較適用於稠密圖 6 */ 7 typedef char vertexType; 8 typedef int edgeType; 9 typedef struct GraphMatrix{ 10 11 int vertexNumber; // 頂點數量 12 int edgeNumber; // 邊的數量 13 vertexType *vertex; // 頂點集合,動態數組 14 edgeType** edge; // 邊集合,二維動態數組 15 16 } GraphMatrix; 17 18 void GraphMatrix_create(GraphMatrix *g);
該頭文件包含了鄰接矩陣的數據結構,結構體的成員變量包括頂點數量、邊的數量、頂點集合和邊的集合。為了節省空間,將頂點集合和邊集設為動態數組,根據頂點數量來分配空間。
實現文件是:GraphStruct.c
1 #include <stdio.h> 2 #include <malloc.h> 3 #include"GraphStruct.h" 4 5 void GraphMatrix_create(GraphMatrix *g){ 6 7 printf("請分別輸入圖的頂點數量和邊的數量,用空格隔開:"); 8 scanf("%d %d", &g->vertexNumber, &g->edgeNumber); //將頂點數量和邊的數量存儲在結構體g中相應的變量 9 g->vertex = (vertexType*)malloc(g->vertexNumber * sizeof(vertexType)); //為動態數組申請空間 10 //二維動態數組申請空間 11 g->edge = (edgeType**)malloc(g->vertexNumber * sizeof(edgeType)); 12 for (int i = 0; i < g->vertexNumber; i++){ 13 g->edge[i] = (edgeType*)malloc(g->vertexNumber * sizeof(edgeType)); 14 } 15 //初始化鄰接矩陣的所有元素 16 for (int i = 0; i < g->vertexNumber; i++){ 17 for (int j = 0; j < g->vertexNumber; j++) 18 g->edge[i][j] = 0; 19 } 20 21 //輸入圖的信息 22 for (int k = 0; k < g->edgeNumber; k++){ 23 24 int i, j; 25 printf("請輸入邊(vi,vj)的下標, i和j,用空格隔開:"); 26 scanf("%d%d", &i, &j); 27 g->edge[i][j] = 1; //(i,j)和(j,i)指的是一條邊 28 g->edge[j][i] = 1; 29 } 30 31 //輸出圖的信息 32 printf("Your graph matrix is :\n"); 33 for (int i = 0; i < g->vertexNumber; i++){ 34 for (int j = 0; j < g->vertexNumber; j++){ 35 printf("%d\t", g->edge[i][j]); 36 } 37 printf("\n"); 38 } 39
測試文件為:main.c
1 #include<stddef.h> 2 #include "GraphStruct.h" 3 4 int main(){ 5 6 GraphMatrix *gm; 7 gm = (GraphMatrix *)malloc(sizeof(GraphMatrix)); 8 GraphMatrix_create(gm); 9 10 11 return 0; 12 }
運行結果為:

圖3 運行結果
對於有向圖,網圖的代碼,只要將上面的代碼稍微修改就行了,本文末尾附上代碼的下載地址。
對於頂點數很多但是邊數很少的圖來說,用鄰接矩陣顯得略為“奢侈”,因為矩陣元素為1的很少,即其中的有用信息很少,但卻占了很大的空間。所以下面我們來看看鄰接表,以圖1的無向圖為例,我們先不講理論的知識,先把圖1的鄰接表畫出來,如圖4。

圖4 鄰接表
作為頂點0,它的鄰接頂點有1,3,4,形成的邊有(0,1),(0,3)和(0,4),所以頂點0將其指出來了;對於頂點1,它的鄰接頂點有0,2,4,所以頂點1將其指出來了,以此類推。他們的邊沒有先后順序之分。對於邊(i,j),鄰接表如下:

圖5 (i,j)的鄰接表
左邊的節點稱為頂點節點,其結構體包含頂點元素和指向第一條邊的指針;右邊的為邊節點,結構體包含邊的頂點對應的下標,和指向下一個邊節點的指針。對於有權值的網圖,只需要在邊節點增加一個權值的成員變量即可。
實現鄰接表存儲結構的代碼如下:
頭文件是:GraphStruct.h
1 /* 2 *圖的另一種存儲結構是鄰接表 3 4 */ 5 typedef struct ListEdgeNode{ 6 int index; // 邊的下標 7 struct ListEdgeNode *next; // 指向下一個節點的指針 8 }ListEdgeNode; 9 10 typedef struct ListVertexNode { 11 vertexType vertex; // 頂點 12 ListEdgeNode *fistEdge; // 指向第一條邊 13 } ListVertexNode; 14 15 typedef struct GraphList{ 16 int vertexNumber; // 頂點的數量 17 int edgeNumber; // 邊的數量 18 ListVertexNode *vertex; // 頂點集合,動態數組 19 }GraphList; 20 21 void GraphList_create(GraphList *g);
該文件定義的結構體如上所述,GraphList是鏈接表的結構體,包含了頂點數,邊數和頂點集,其中頂點集根據頂點個數分配內存空間。
實現文件是:GraphStruct.c
void GraphList_create(GraphList *g){ printf("請分別輸入圖的頂點數量和邊的數量,用空格隔開:"); scanf("%d %d", &g->vertexNumber, &g->edgeNumber); //將頂點數量和邊的數量存儲在結構體g中相應的變量 //為動態數組申請空間 g->vertex = (ListVertexNode*)malloc(g->vertexNumber * sizeof(ListVertexNode)); //初始化頂點指的第一條邊 for (int i = 0; i < g->edgeNumber; i++){ g->vertex[i].fistEdge = NULL; } //輸入圖的信息 ListEdgeNode *listEdgeNode; for (int k = 0; k < g->edgeNumber; k++){ int i, j; printf("請輸入邊(vi,vj)的下標, i和j,用空格隔開:"); scanf("%d%d", &i, &j); //始終將插入的節點放在頂點所指的地一條邊 listEdgeNode = (ListEdgeNode *)malloc(sizeof(ListEdgeNode)); listEdgeNode->index = j; listEdgeNode->next = g->vertex[i].fistEdge; g->vertex[i].fistEdge = listEdgeNode; listEdgeNode = (ListEdgeNode*)malloc(sizeof(ListEdgeNode)); listEdgeNode->index = i; listEdgeNode->next = g->vertex[j].fistEdge; g->vertex[j].fistEdge = listEdgeNode; } //輸出圖的信息 ListEdgeNode * len = NULL; for (int i = 0; i < g->vertexNumber; i++){ if (g->vertex[i].fistEdge != NULL) len = g->vertex[i].fistEdge; while (len!= NULL){ printf("%d --- %d\t", i,len->index); len = len->next; } printf("\n"); } }
測試文件是:main.c
1 #include<stddef.h> 2 #include "GraphStruct.h" 3 4 int main(){ 5 6 7 GraphList *gl; 8 gl = (GraphList*)malloc(sizeof(GraphList)); 9 GraphList_create(gl); 10 return 0; 11 }
運行結果為:

鄰接矩陣適合於點少邊多的圖,而對於邊少的圖,可以考慮用鄰接表。但是對於有向圖,鄰接表的表示有多種,有些更為復雜,在這里不一一闡述,有興趣的可以跟本人交流。
源代碼下載:http://pan.baidu.com/s/1hqeYahQ
