圖的存儲結構相比較線性表與樹來說就復雜很多,對於線性表來說,是一對一的關系,所以用數組或者鏈表均可簡單存放。樹結構是一對多的關系,所以我們要將數組和鏈表的特性結合在一起才能更好的存放。
那么我們的圖,是多對多的情況,另外圖上的任何一個頂點都可以被看作是第一個頂點,任一頂點的鄰接點之間也不存在次序關系。
仔細觀察以下幾張圖,然后深刻領悟一下:
因為任意兩個頂點之間都可能存在聯系,因此無法以數據元素在內存中的物理位置來表示元素之間的關系(內存物理位置是線性的,圖的元素關系是平面的)。
如果用多重鏈表來描述倒是可以做到,但在幾節課前的樹章節我們已經討論過,純粹用多重鏈表導致的浪費是無法想像的(如果各個頂點的度數相差太大,就會造成巨大的浪費)。
鄰接矩陣(無向圖)
考慮到圖是由頂點和邊或弧兩部分組成,合在一起比較困難,那就很自然地考慮到分為兩個結構來分別存儲。
頂點因為不區分大小、主次,所以用一個一維數組來存儲是狠不錯的選擇。
而邊或弧由於是頂點與頂點之間的關系,一維數組肯定就搞不定了,那我們不妨考慮用一個二維數組來存儲。
圖的鄰接矩陣(Adjacency Matrix)存儲方式是用兩個數組來表示圖。一個一維數組存儲圖中頂點信息,一個二維數組(稱為鄰接矩陣)存儲圖中的邊或弧的信息。
我們可以設置兩個數組,頂點數組為vertex[4]={V0,V1,V2,V3},邊數組arc[4][4]為對稱矩陣(0表示不存在頂點間的邊,1表示頂點間存在邊)。
對稱矩陣:所謂對稱矩陣就是n階矩陣的元滿足a[i][j]=a[j][i](0<=i,j<=n)。即從矩陣的左上角到右下角的主對角線為軸,右上角的元與左下角相對應的元全都是相等的。
有了這個二維數組組成的對稱矩陣,我們就可以很容易地知道圖中的信息:
- 要判定任意兩頂點是否有邊無邊就非常容易了;
- 要知道某個頂點的度,其實就是這個頂點Vi在鄰接矩陣中第i行(或第i列)的元素之和;
- 求頂點Vi的所有鄰接點就是將矩陣中第i行元素掃描一遍,arc[i][j]為1就是鄰接點咯。
鄰接矩陣(有向圖)
無向圖的邊構成了一個對稱矩陣,貌似浪費了一半的空間,那如果是有向圖來存放,會不會把資源都利用得很好呢?
可見頂點數組vertex[4]={V0,V1,V2,V3},弧數組arc[4][4]也是一個矩陣,但因為是有向圖,所以這個矩陣並不對稱,例如由V1到V0有弧,得到arc[1][0]=1,而V0到V1沒有弧,因此arc[0][1]=0。
另外有向圖是有講究的,要考慮入度和出度,頂點V1的入度為1,正好是第V1列的各數之和,頂點V1的出度為2,正好是第V1行的各數之和。
鄰接矩陣(網)
在圖的術語中,我們提到了網這個概念,事實上也就是每條邊上帶有權的圖就叫網。

代碼如下:
#include <stdio.h> #define MaxVex 100 //最大頂點數 #define INFINITY 65535 //表示∞ #define TRUE 1 #define FALSE 0 typedef char VertexType; //頂點類型 typedef int EdgeType; //權值類型 typedef int Bool; Bool visited[MaxVex]; typedef struct { VertexType vexs[MaxVex]; //頂點數組 EdgeType arc[MaxVex][MaxVex]; //鄰接矩陣 int numVertexes, numEdges; //當前圖中的結點數以及邊數 }MGraph; //廣度優先遍歷需要的循環隊列 typedef struct { int data[MaxVex]; int front, rear; }Queue; /****************************************/ //隊列的相關操作 //初始化 void InitQueue(Queue *Q) { Q->front = Q->rear = 0; } //入隊 void EnQueue(Queue *Q, int e) { if ((Q->rear+1)%MaxVex == Q->front) return ; Q->data[Q->rear] = e; Q->rear = (Q->rear+1)%MaxVex; } //判空 Bool QueueEmpty(Queue *Q) { if (Q->front == Q->rear) return TRUE; else return FALSE; } //出隊 void DeQueue(Queue *Q, int *e) { if (Q->front == Q->rear) return ; *e = Q->data[Q->front]; Q->front = (Q->front+1)%MaxVex; } /****************************************/ //建立圖的鄰接矩陣 void CreateMGraph(MGraph *G) { int i, j, k, w; printf("輸入頂點數和邊數: "); scanf("%d%d", &G->numVertexes,&G->numEdges); fflush(stdin); printf("==============================\n"); printf("輸入各個頂點:\n"); for (i=0; i<G->numVertexes; ++i) { printf("頂點%d: ",i+1); scanf("%c", &G->vexs[i]); fflush(stdin); } for (i=0; i<G->numVertexes; ++i) { for (j=0; j<G->numVertexes; ++j) G->arc[i][j] = INFINITY; } printf("==============================\n"); for (k=0; k<G->numEdges; ++k) { printf("輸入邊(vi, vj)中的下標i和j和權W: "); scanf("%d%d%d", &i,&j,&w); G->arc[i][j] = w; G->arc[j][i] = G->arc[i][j]; } } //輸出 void DisMGraph(MGraph *G) { int i, j, k; k = G->numVertexes; for (i=0; i<k; ++i) { for (j=0; j<k; ++j) { printf("%5d ", G->arc[i][j]); } putchar('\n'); } } /****************************************/ //圖的深度優先遍歷 void DFS(MGraph G, int i) { int j; visited[i] = TRUE; printf("%c ", G.vexs[i]); for (j=0; j<G.numVertexes; ++j) { if (G.arc[i][j]!=INFINITY && !visited[j]) DFS(G, j); } } void DFSTraverse(MGraph G) { int i; for (i=0; i<G.numVertexes; ++i) visited[i] = FALSE; for (i=0; i<G.numVertexes; ++i) { if (!visited[i]) DFS(G, i); } } //圖的廣度優先遍歷 void BFSTraverse(MGraph *G) { int i, j; Queue Q; for (i=0; i<G->numVertexes; ++i) visited[i] = FALSE; InitQueue(&Q); for (i=0; i<G->numVertexes; ++i) { if (!visited[i]) { visited[i] = TRUE; printf("%c ", G->vexs[i]); EnQueue(&Q, i); while (!QueueEmpty(&Q)) { DeQueue(&Q, &i); for (j=0; j<G->numVertexes; ++j) { if (!visited[j] && G->arc[i][j]!=INFINITY) { visited[j] = TRUE; printf("%c ", G->vexs[j]); EnQueue(&Q, j); } } } } } } /****************************************/ //程序入口 int main(){ MGraph G; CreateMGraph(&G); printf("\n圖的深度優先遍歷為: "); DFSTraverse(G); printf("\n圖的廣度優先遍歷為: "); BFSTraverse(&G); printf("\n"); return 0; }
運行結果截圖:





