數據結構(45)圖的四種存儲方法:鄰接矩陣法、鄰接表法、十字鏈表、鄰接多重表

圖的存儲必須要完整、準確地反映頂點集合邊集的信息,根據不同圖的結構和算法,採用不同的存儲方式將對程序的效率產生相當大的影響,因此所選的存儲結構應適合於欲求解的問題。

目錄

1、鄰接矩陣法

2、鄰接表法

3、十字鏈表

4、鄰接多重表


1、鄰接矩陣法

所謂鄰接矩陣存儲,是指用一個一維數組存儲圖中頂點的信息,用一個二維數組存儲圖中邊的信息(即各頂點之間的鄰接關係),存儲頂點之間鄰接關係的二維數組稱爲鄰接矩陣。

結點數爲n的圖G=(V,E)的鄰接矩陣A是n*n的。將G的頂點編號爲v1,v2,...,vn。若(vi,vj)∈E,則A[i][j] = 1,否則A[i][j] = 0。

對於帶權圖而言,若頂點vi和vj之間有邊相連,則鄰接矩陣中對應項應該存放着該邊的權值,若頂點vi和vj不相連,則用來代表兩個頂點之間不存在邊:

有向圖、無向圖的網對應的鄰接矩陣示例如下圖所示:

     

                                                              圖45-2  有向圖、無向圖及網的鄰接矩陣                              

圖的鄰接矩陣存儲結構定義如下:

#define MaxVertexNum 100  //頂點數目的最大值
typedef char VertexType;  //頂點的數據類型
typedef int EdgeType;     //帶權圖中邊上權值的數據類型
typedef struct {
	VertexType Vex[MaxVertexNum]; //頂點表
	EdgeType Edge[MaxVertexNum][MaxVertexNum]; //鄰接矩陣,邊表
	int vexnum, arcnum; //圖的當前頂點數和弧數
}MGraph;

注意:

① 在簡單應用中,可直接用二維數組作爲圖的鄰接矩陣(頂點信息等均可忽略)。

② 當鄰接矩陣中的元素僅表示相應的邊是否存在時,EdgeType可定義爲值爲0和1的枚舉類型。

③ 無向圖的鄰接矩陣是對稱矩陣,對規模特大的鄰接矩陣可採用壓縮存儲。

④ 鄰接矩陣表示法的空間複雜度爲O(n^{2}),其中n爲圖的頂點數|V|。

圖的鄰接矩陣存儲表示法具有以下特點:

① 無向圖的鄰接矩陣一定是一個對稱矩陣(並且唯一)。因此,在實際存儲鄰接矩陣時只需存儲上(或下)三角矩陣的元素。

② 對於無向圖,鄰接矩陣的第i行(或第i列)非零元素(或非∞元素)的個數正好是第i個頂點的度TD(vi)。

③ 對於有向圖,鄰接矩陣的第i行(或第i列)非零元素(或非∞元素)的個數正好是第i個頂點的出度OD(vi)[或入度ID(vi)]

④ 用鄰接矩陣法存儲圖,很容易確定圖中任意兩個頂點之間是否有邊相連。但是,要確定圖中有多少條邊,則必須按行、按列隊每個元素進行檢測,所花費的代價很大。

⑤ 稠密圖適合使用鄰接矩陣存儲,因爲空間利用率高

⑥ 設圖G的鄰接矩陣爲A,A^{n}的元素A^{n}[i][j]等於由頂點i到頂點j的長度爲n的路徑的數目。

2、鄰接表法

當一個圖爲稀疏圖時,使用鄰接矩陣法顯然要浪費大量的存儲空間,而圖的鄰接表法結合了順序存儲和鏈式存儲方法,大大減少了這種不必要的浪費。

所謂鄰接表,是指對圖G中的每個頂點vi建立一個單鏈表,第i個單鏈表中的結點表示依附於頂點vi的邊(對於有向圖則是以頂點vi爲尾的弧),這個單鏈表就稱爲頂點vi的邊表(對於有向圖則稱爲出邊表)。邊表的頭指針和頂點的數據信息採用順序存儲(稱爲頂點表),所以在鄰接表中存在兩種結點:頂點表結點和邊表結點,如下圖所示。

                                                  圖45-2  頂點表和邊表結點結構

頂點表結點由頂點域(data)和指向第一條鄰接邊的指針(firstarc)構成,邊表(鄰接表)結點由鄰接點域(adjvex)和指向下一條鄰接邊的指針域(nextarc)構成。

無向圖和有向圖的實例分別如圖45-3和45-4所示。

               

                                            圖45-3  無向圖鄰接表示法

                                                        圖45-4  有向圖鄰接表示法 

圖的鄰接表存儲結構定義如下:

typedef struct ArchNode   //邊表結點
{
	int adjvex;   //該弧所指向弧點的位置
	struct ArchNode *next; //指向下一條弧的指針
	int info; //網的邊權重
}ArchNode;
typedef struct VNode
{
	VertexType data;
	ArchNode *first;  //指向第一條依附於該頂點的弧的指針
}VNode,AdjList[MaxVertexNum];
typedef struct {
	AdjList vertices;   //鄰接表
	int vexnum, arcnum; //圖的當前頂點數和弧數
}MGraph;

圖的鄰接表存儲方法具有以下特點:

① 若G爲無向圖,則所需的存儲空間爲O(|V|+2|E|);若G爲有向圖,則所需的存儲空間爲O(|V|+|E|)。

② 對於稀疏圖,採用鄰接表表示將極大節省存儲空間。

③ 在鄰接表中,給定一頂點,能很容易的找出它所有的鄰邊,因爲只需要讀取它的鄰接表。在鄰接矩陣中,相同的操作則需要掃描一行,花費的時間爲O(n)。但是,若要確定給定的兩個頂點間是否存在邊,則在鄰接矩陣中可以立刻查到,而在鄰接矩陣中則需要在相應結點對應的邊表中查找另一結點,效率較低。

④ 在有向圖的鄰接表示中,求一則需要遍歷全部的鄰接表個給定結點的出度只需計算其鄰接表中的結點個數;但求其頂點的入度則需要遍歷全部的鏈接表,因此,也有人採用逆鄰接表(任一表頭結點下的邊結點的數量是圖中該結點入度的弧的數量,與鄰接表相反。圖的逆鄰接表反映的是節點的入度鄰接情況。)的存儲方式來加速求解給定結點的入度。

⑤ 圖的鄰接表並不唯一,因爲在每個頂點對應的單鏈表中,各邊結點的鏈接次序可以是任意的,它取決於建立鏈接表的算法及邊的輸入次序。

3、十字鏈表

十字鏈表是有向圖的一種存儲結構。在十字鏈表中,對於有向圖中的每條弧有一個結點,對應於每個頂點也有一個結點。這些結點的結構如下圖所示。

弧結點中有5個域:尾域(tailvex)和頭域(headvex)分別指示弧尾和弧頭這兩個頂點在圖中的位置:鏈域hlink指向弧頭相同的下一條弧;鏈域tlink指向弧尾相同的下一條弧;info域指向該弧的相關信息(比如權重)。這樣,弧頭相同的弧就在同一個鏈表上,弧尾相同的弧也在一個鏈表上。

頂點域中有三個域:data域存放頂點相關的數據信息,如頂點名稱;firstin和firstout兩個域分別指向以該頂點爲弧頭或弧尾的第一個弧結點。

圖45-5爲有向圖的十字鏈表表示法。注意,頂點結點之間是順序存儲的。

                                             圖45-5  有向圖的十字鏈表表示

我來大致解釋一下這個圖

圖中4個頂點結點0,1,2,3對應圖中的V1,V2,V3,V4。7個弧結點對應7條有向邊。比如tailvex=0,headvex=1代表從V1-V2的有向邊。

V1:V1的firstin指向以V3-V1有向邊的弧結點,然後V3-V1有向邊的弧結點裏的hlink指向V3-V1結點。至此關於V1結點作爲弧頭的信息構造完畢。

        V1的firstout指向V1-V2有向邊的弧結點,然後V3-V1有向邊的弧結點的hlink指向V1-V3有向邊的弧結點。至此關於V1的信息構造完畢。

V2、V3、V4就不廢話了。

在十字鏈表中,既容易找到Vi爲尾的弧,又容易找到Vi爲頭的弧,因而容易求得頂點的出入度。

4、鄰接多重表

鄰接多重表是無向圖的另一種鏈式存儲結構。

在鄰接表中,容易求得頂點和邊的各種信息,但在鄰接表中兩個結點之間是否存在邊而對邊執行刪除操作的時候,需要分別在兩個頂點的邊表中遍歷,效率較低。

與十字鏈表類似,在鄰接多重表中,每條邊用一個結點表示,其結構如下所示。

mark:標誌域,可用以標記該條邊是否被搜索過

ivex/jvex:該邊依附的兩個頂點在圖中的位置

ilink:指向下一條依附於頂點ivex的邊

jlink:指向下一條依附於頂點jviex的邊

info:指向和邊有關的各種信息的指針域

每個結點也可用一個結點表示,它是由如下所示的兩個域組成。

data:頂點的相關信息

firstedge:第一條依附於該頂點的邊

在鄰接多重表中,所有依附於同一個頂點的邊串聯在同一鏈表中,由於每條邊依附於兩個頂點,因此每個邊結點同時連接在兩個鏈表中。對無向圖而言,其鄰接多重表和鄰接表的差別僅在於,同一條邊在鄰接表中用兩個結點表示。而在鄰接多重表中只有一個結點。

圖45-6所示爲無向圖的鄰接多重表表示法。鄰接多重表的各種基本操作的實現和鄰接表類似,

                                           圖46-6  無向圖的鄰接多重表表示

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章