圖 續1
------------------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 列,然後在相應的位置上用 0、1 來表
達頂點之間的 邊 或 弧
「也可以定義為一個 一維數組 …」
通過結構體來表達鄰接矩陣,如下:
對於圖 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