1. 程式人生 > >圖 續1

圖 續1

存儲空間 結構體 地址 二維 只需要 全部 利用 logs 進行

------------------siwuxie095

圖的存儲結構

這裏介紹 圖的存儲結構,也稱為 圖的表示法

畢竟,圖畫出來並不是為了好玩,而是要用這些圖去一些實際問題,

那麽要讓這些圖去解決實際問題,該怎麽利用它呢?

第一步,就必須要把 變成 數據,而這些數據又能真實的反映出圖

中的 頂點與邊 頂點與弧 之間的關系(這裏介紹的也正是第一步)

· 對於有向圖來說,它是由 頂點和弧 組成的

· 對於無向圖來說,它是由 頂點和邊 組成的

技術分享

所以,去存儲有向圖和無向圖時,在存儲算法上,會有一定的差別

關於圖的存儲算法,有 4 種比較常用,其中包括 鄰接矩陣鄰接表

十字鏈表 鄰接多重表

技術分享

· 鄰接矩陣:采用數組進行存儲,用來記錄 有向圖 無向圖

· 鄰接表:采用鏈表進行存儲,用來記錄 有向圖

· 十字鏈表:采用鏈表進行存儲,用來記錄 有向圖

· 鄰接多重表:采用鏈表進行存儲,用來記錄 無向圖

不論是什麽樣的存儲方式,它都是要去存儲 頂點與邊 頂點與弧

在介紹這 4 種存儲結構之前,先要明晰三個重要概念:弧尾弧頭 權值

技術分享

對於上圖中的箭頭來說:

箭頭的尾端(起端) 弧尾,箭頭的頭端(終端) 弧頭

而對於一張圖來說:

箭頭本身就表示從某一頂點到達某一頂點,則箭頭本身有 權值

如:一個城市到另一個城市的道路是 300 公裏,就可以把權值

記為 300

總之,權值 是一個抽象數據,它用來表示 上的數據,

從而能夠為後續的算法提供算法依據

鄰接矩陣

鄰接矩陣,它采用數組存儲,用來記錄 有向圖 無向圖

技術分享

1)對於有向圖:

技術分享

有向圖的頂點表達起來很簡單,只需存儲 頂點索引 頂點數據

「頂點索引 - 不可重復」

弧的表示方法就用到了鄰接矩陣

顯然,第一種算法的重點在於 鄰接矩陣,也就是 弧的表示算法 上

如下:

技術分享

有向圖中有四個頂點 v1、v2、v3、v4,把頂點之間的弧,用 1 表示,

而沒有弧的地方,用 0 表示

於是,可以將 v1 到 v4 這四個頂點在矩陣當中分別列開

顯然,v1 和 v1 本身是不可以到達的,就寫上 0,而 v2 和 v2、v3 和 v3、

v4 和 v4,同理 … 都寫上 0,所以主對角線上都是 0

除了自身不能到達自身之外,自身到達其他頂點的情況,如下:

v1 到 v2、v3、v4 各有一條弧,所以把 v1 到 v2、v1 到 v3、

v1 到 v4 都記為 1

v2 到 v1、v3、v4 都沒有相應的弧,所以把 v2 到 v1、v2 到 v3、

v2 到 v4 都記為 0

v3 只有到 v4 的弧,而沒有到 v1、v2 的弧,所以把 v3 到 v4 記為 1

v3 到 v1、v3 到 v2 記為 0

v4 只有到 v1 的弧,而沒有到 v2、v3 的弧,所以把 v4 到 v1 記為 1

v4 到 v2、v4 到 v3 記為 0

如果 v1 到 v2 不僅有弧,還有權值,那麽也可以直接記為 權值,而不是 1

其它同理

這就是有向圖的鄰接矩陣表達法

2)對於無向圖:

技術分享

無向圖與有向圖有著不同:

無向圖中,v1 到 v2 與 v2 到 v1 用的是同一條邊

所以,v1 到 v2 記為 1,而 v2 到 v1 也記為 1

其它 … 同理 …

不難發現,用鄰接矩陣去記錄無向圖的邊時,主對角線

上方與下方是完全對稱

由此可見,在一個無向圖中,記錄它的鄰接矩陣時,如果

想要節省空間,可以只記錄鄰接矩陣的上三角部分,或

三角部分,這樣可以節省一些存儲空間

當然,也可以都把它記錄下來

那麽鄰接矩陣如何轉換成代碼中的語句呢?

可以直接把 鄰接矩陣 定義成一個 二維數組 即可,

4 4 列,然後在相應的位置上用 01 來表

達頂點之間的

「也可以定義為一個 一維數組 …」

通過結構體來表達鄰接矩陣,如下:

技術分享

對於圖 Map 本身來說,需要存儲 頂點數組 和 鄰接矩陣

對於頂點 Node 來說,需要存儲 頂點索引 和 頂點數據

頂點數組中記錄的是所有的頂點,鄰接矩陣中記錄的是

所有的邊,以及頂點與邊的關系(或 所有的弧,以及頂

點與弧的關系)

鄰接表

鄰接表,它采用鏈式存儲,用來記錄 有向圖

技術分享

對於有向圖:

技術分享

有向圖的頂點需要存儲 頂點索引頂點數據 出弧鏈表頭指針

與前面的鄰接矩陣相比,多出了一個 出弧鏈表頭指針,那麽什麽是

出弧鏈表頭指針 呢?

以頂點 v1 為例:

v1 一共有三條出弧,分別指向 v2、v3、v4

v1 的三條出弧形成一個鏈表,即 出弧鏈表,而 出弧鏈表的頭結點 即 v1

於是當拿到頭節點後,就通過頭節點中的出弧鏈表頭指針,依次訪問到 v1

的三條弧,而且都是出弧

「註意:頭結點沒有意義,只起牽頭作用」

有向圖的弧需要表達成 結點,且全部理解為 出弧(相對),它需要存儲

弧頭頂點索引下一條弧指針弧數據

v1 到 v2 的弧 為例: v2 是弧頭,v1 是弧尾

對於 v1 來說, 它上面記錄了出弧鏈表頭指針,於是可以通過鏈表頭指針

去找到這條弧,而這條弧又記錄了 弧頭頂點索引 ,即 v2 的索引

而 v1 到 v2 的弧上記錄的 下一條弧指針 和 弧數據,分別是 v1 到 v3 這

條弧的地址 和 v1 到 v2 這條弧的權值

顯然,只要有了 頂點的表示方法 弧的表示方法,

就能把整個圖表達出來,如下:

技術分享

上面一排是四個頂點 v1、v2、v3、v4,假設它們的索引分別是 0、1、2、3

每個頂點下面形成的鏈表就是 出弧鏈表,它的結點分為:頭結點 弧結點

頭結點 當前頂點,它沒有任何意義,只起牽頭作用,便於尋址,

其中的 next 即為出弧鏈表頭指針

弧結點 當前頂點的所有出弧,其中的 next 為下一條弧指針

data 為弧的權值,而最前面的即為弧頭頂點索引

對於 v1 來說,它的 next 指向 v1 到 v2 的弧,這條弧上有 v2 的索引 1,

同時,這條弧的 next 指向 v1 到 v3 的弧 … 最後一條弧的 next 一定要

指向 NULL

對於 v2 來說,它沒有任何一條出弧,所以它的 next 直接指向 NULL

對於 v3 來說,同理 …

對於 v4 來說,同理 …

逆鄰接表

與鄰接表相對應的概念,叫做 逆鄰接表

技術分享

所謂 逆鄰接表,是相對於 鄰接表 來說的,二者的區別在於:

1)鄰接表的頂點中記錄的是出弧鏈表頭指針

它指向的是 當前頂點 當前頂點的出弧 所形成的出弧鏈表

出弧鏈表的弧結點中記錄的是 弧頭頂點索引

2)逆鄰接表的頂點中記錄的是入弧鏈表頭指針

它指向的是 當前頂點 當前頂點的入弧 所形成的入弧鏈表

入弧鏈表的弧結點中記錄的是 弧尾頂點索引

之所以要改成 弧尾頂點索引,是因為頂點中的記錄的是入弧

鏈表頭指針,對於一個弧來說,相當於它的弧頭已經確定了,

則弧中就不需要再記錄弧頭了,直接記錄弧尾即可

所以,逆鄰接表 鄰接表 相對

通過結構體來表達鄰接表,如下:

技術分享

對於圖 Map 本身來說,只需要存儲 頂點數組 即可

對於頂點 Node 來說,需要存儲 頂點索引、頂點數據 和 該頂點弧鏈表的頭結點

該頂點弧鏈表的頭結點 就與該頂點的弧所連接,這條弧又能去找到下一條弧

對於弧 Arc 來說,需要存儲 指向的頂點索引、指向下一條弧的指針 弧信息

十字鏈表

十字鏈表,它采用鏈式存儲,用來記錄 有向圖

技術分享

對於有向圖:

技術分享

有向圖的頂點需要存儲 頂點索引頂點數據以該頂點為弧尾的弧結點指針

以該頂點為弧頭的弧結點指針

v1 為例:

v1 中要記錄的除了 v1 的索引和數據外,還要記錄第一條形成的

v1 射出去的弧 第一條從其它頂點射回來的弧

假設 v1 到 v2 的弧是第一條從 v1 射出去的弧,v4 到 v1 的弧是

第一條從其它頂點射回來的弧,則:

v1 為弧尾的弧結點指針就指向 v1 到 v2 的弧,以 v1 為弧頭的

弧結點指針就指向 v4 到 v1 的弧

有向圖的弧則需要存儲 弧尾頂點索引弧頭頂點索引弧數據

弧尾相同的下一條弧的指針 弧頭相同的下一條弧的指針

v1 到 v2 的弧 為例:

弧尾頂點索引 v1,弧頭頂點索引 即 v2,弧數據 即 弧的權值

弧尾相同的下一條弧,即 v1 到 v3 的弧 或 v1 到 v4 的弧,如果

該指針指向 v1 到 v3 的弧,則 v1 到 v3 的弧上相應位置的指針

就指向 v1 到 v4 的弧

弧頭相同的下一條弧,不存在,所以將該指針置為 NULL

通過結構體來表達十字鏈表,如下:

技術分享

對於圖 Map 本身來說,只需要存儲 頂點數組 即可

對於頂點 Node 來說,需要存儲 頂點索引、頂點數據、

第一條入弧節點指針 和 第一條出弧結點指針

其中:第一條入弧 出弧 結點指針 的類型正是 弧類型

對於弧 Arc 來說,需要存儲 弧尾頂點索引、弧頭頂點索引、

指向下一條弧頭相同的弧的指針、指向下一條弧尾相同的弧

的指針、以及弧信息

索引可以是字母 char 類型,也可以是數字 int 類型,還可以

是任意一種能夠不重復的類型

而指向弧的指針必然就是弧本身的指針,即 Arc 類型

鄰接多重表

鄰接多重表,它采用鏈式存儲,用來記錄 無向圖

技術分享

對於無向圖:

技術分享

無向圖的頂點需要存儲 頂點索引頂點數據 連接該頂點的邊

v1 為例:

v1 中要記錄的除了 v1 的索引和數據外,還要記錄的邊有 3 條,

即 v1 到 v2 的邊、v1 到 v3 的邊、v1 到 v4 的邊

實際上要記錄的是指向第一條邊的指針,假設 v1 到 v2 的邊是

第一條邊,則記錄指向這條邊的指針即可

而無向圖的邊需要存儲 A 頂點索引B 頂點索引邊數據

與 A 頂點相連接的下一條邊的指針 與 B 頂點相連接的

一條邊的指針

v1 到 v2 的邊 為例:

A 頂點索引 即 v1 的索引,B 頂點索引 即 v2 的索引,邊數據 即 邊的權值

與 A 頂點相連接的下一條邊,與 v1 相連接的下一條邊,即 v1 到 v3 的邊

或 v1 到 v4 的邊,如果該指針指向 v1 到 v3 的邊,則 v1 到 v3 的邊上相

應位置的指針就指向 v1 到 v4 的邊,這取決於哪條邊先建立起來的

與 B 頂點相連接的下一條邊,即 與 v2 相連接的下一條邊,不存在,所以

將該指針置為 NULL

通過結構體來表達鄰接多重表,如下:

技術分享

對於圖 Map 本身來說,只需要存儲 頂點數組 即可

對於頂點 Node 來說,需要存儲 頂點索引、頂點數據 和 第一條邊結點指針

第一條邊的結點指針就意味著能找到一條邊

對於邊 Edge 來說,需要存儲 頂點 A 的索引、頂點 B 的索引、邊信息、

連接 A 的下一條邊的指針 和 連接 B 的下一條邊的指針

索引可以是字母 char 類型,也可以是數字 int 類型,還可以是任意一種

能夠不重復的類型

而連接邊的指針必然就是邊本身的指針,即 Edge 類型

【made by siwuxie095】

圖 續1