圖是由頂點和邊或弧兩部分來組成,所以分兩個結構來分別存儲。
1、鄰接矩陣
圖的鄰接矩陣存儲方式是用兩個數組來表示圖。一個一維數組存儲圖中頂點信息,一個二維數組(稱爲鄰接矩陣)存儲圖中的邊或弧的信息。
無向圖的邊數組是一個對稱矩陣。有了這個矩陣,我們就可以知道無向圖的信息:
(1)要判斷任意兩個頂點是否有邊無邊,只需要查找arc[i][j]或arc[j][i]是否爲1即可。
(2)要知道某個頂點的度,其實就是這個頂點vi在鄰接矩陣中第i行(或第i列)的元素之和。
(3)求頂點vi的所有鄰接點就是將矩陣中第i行的元素掃描一遍,arc[i][j]爲1就是鄰接點。
有向圖講究出度和入度,頂點vi的入度是是第i列的各數之和,頂點vi的出度是第i行的各數之和。
對於網來說,把權值存儲在arc[i][j]中。如果邊或弧不存在,用一個不可能的值來代表不存在。
//鄰接矩陣的結構定義
typedef char VertexType;
typedef int EdgeType;
#define MAXVEX 100
#define INFINITY 65535
typedef struct
{
VertexType vexs[MAXVEX];
EdgeType arc[MAXVEX][MAXVEX];
int numVertexes, numEdges;
} MGraph;
//建立無向網的鄰接矩陣表示
void CreateMGraph(MGraph *G)
{
int i, j, k, w;
printf("輸入頂點數和邊數:\n");
scanf("%d, %d", &G->numVertexes, &G->numEdges);
for (i = 0; i < G->numVertexes; i++) //讀入頂點信息建立頂點表
scanf(&G->vexs[i]);
for (i = 0; i < G->numVertexes; i++)
for (j = 0; j < G->numVertexes; j++)
G->arc[i][j] = INFINITY;
for (k = 0; k < G->numEdges; k++)
{
printf("輸入邊(vi, vj)的下標i,下標j和權w:\n");
scanf("%d, %d, %d", &i, &j, &w);
G->arc[i][j] = w;
G->arc[j][i] = G->arc[i][j];
}
}
2、鄰接表
鄰接表的處理辦法:
(1)圖中頂點用一個一維數組存儲。對於頂點數組中每個數據元素還需要存儲指向第一個鄰接點的指針,以便查找該 頂點的邊信息。頂點表中的各個結點由data和firstedge兩個域表示,data是數據域,存儲頂點信息,firstedge是指針域,指向邊表的第一個頂點,即此頂點的第一個鄰接點。
(2)圖中每個頂點vi的所有鄰接點構成一個線性表,由於鄰接點的個數不定,所以用單鏈表存儲,無向圖稱爲頂點vi的邊表,有向圖則稱爲頂點vi作爲弧尾的出邊表。邊表姐弟由adjvex和next兩個域組成。adjvex是鄰接點域,存儲某個頂點的鄰接點在頂點表中的下標,next則存儲指向邊表中下一個結點的指針。
對於這樣的結構,我們要獲得相關信息是方便的:
(1)要想知道某個頂點的度,就去查找這個頂點的邊表中結點的個數。
(2)判斷頂點vi和vj是否存在邊,只需要測試頂點vi的邊表中adjvex是否存在結點vj的下標。
(3)求頂點的所有鄰接點,其實就是對此頂點的邊表進行遍歷,得到的adjvex域對應的頂點就是鄰接點。
以頂點爲弧尾來存儲邊表的,可以很容易得到每個頂點的出度。有時爲了便於確定頂點的入度或以頂點爲弧頭的弧,可以建立一個有向圖的逆鄰接表,即對每個頂點vi都建立 一個鏈接爲vi爲弧頭的表。
對於帶權值的網圖,可以在邊表結點定義中在增加一個weight的數據域。
//鄰接表結構
typedef char VertexType;
typedef int EdgeType;
typedef struct EdgeNode
{
int adjvex;
EdgeType weight;
struct EdgeNode *next;
} EdgeNode;
typedef struct VertexNode
{
VertexType data;
EdgeNode *firstedge;
} VertexNode, AdjList[MAXVEX];
typedef struct
{
AdjList adjList;
int numVertexes, numEdges;
} GraphAdjList;
//無向圖鄰接表的創建
void CreateALGraph(GraphAdjList *G)
{
int i, j, k;
EdgeNode *e;
printf("輸入頂點數和邊數:\n");
scanf("%d, %d", &G->numVertexes, &G->numEdges);
for (i = 0; i < G->numVertexes; i++)
{
scanf(&G->adjList[i].data);
G->adjList[i].firstedge = NULL;
}
for (k = 0; k < G->numEdges; k++)
{
printf("輸入邊(vi,vj)上的頂點序號:\n");
scanf("%d, %d", &i, &j);
e = (EdgeNode *)malloc(sizeof(EdgeNode));
e->adjvex = j;
e->next = G->adjList[i].firstedge;
G->adjList[i].firstedge = e;
e = (EdgeNode *)malloc(sizeof(EdgeNode));
e->adjvex = i;
e->next = G->adjList[j].firstedge;
G->adjList[j].firstedge = e;
}
}
3、十字鏈表(針對有向圖的)
頂點表結點結構:data存儲頂點信息;firstin表示入邊表的頭指針,指向該頂點的入邊表的第一個結點;firstout表示出邊表頭指針,指向該結點的出邊表中的第一個結點。
邊表結點結構:tailvex是指弧起點在頂點表的下標;headvex是指弧終點在頂點表中的下標;headlink是指入邊表指針域,指向終點相同的下一條邊;taillink是指邊表指針域,指向起點相同的下一條邊,如果是網,還可以再增加一個weight域來存儲權值。
十字鏈表的好處就是因爲吧鄰接表和逆鄰接表整合在一起,這樣既容易找到以vi爲尾的弧,也容易找到以vi爲頭的弧,因此容易求得頂點的出度和入度。
4、鄰接多重表
與鄰接表相比,重新定義邊表結點的結構:ivex和jvex是與某條邊依附的兩個頂點在頂點表中的下標。ilink指向依附頂點ivex的下一條邊,jlink指向依附頂點jvex的下一條邊。
鄰接多重表語鄰接表的差別僅僅是在於同一條邊在鄰接表中用兩個階段表示,而在鄰接表中只有一個結點。
5、邊集數組
邊集數組是由兩個一維數組構成。一個是存儲頂點的信息;另一個是存儲邊的信息,這個邊數組每個數據元素由一條邊的起點下標,終點下標和權組成。
顯然邊集數組關注的是邊的集合,在邊集數組中查找一個頂點的都,需要遍歷整個邊數組,效率不高。