圖
圖(graph)是一種非線性資料結構,由頂點(vertex)和邊(edge)組成。我們可以將圖G抽象地表示為一組頂點V和一組邊E的集合。以下示例展示了一個包含5個頂點和7條邊的圖。
如果將頂點看做節點,將邊看做連線各個節點的引用(指標),我們就可以將圖看作一種從連結串列擴充而來的資料結構。如下圖,相較於線性關係(連結串列)和分治關係(樹),網路關係(圖)的自由度更高,因而更為複雜。
根據邊是否具有方向,可分為無向圖(undirected graph)和有向圖(directed graph)。
- 在無向圖中,邊表示兩頂點之間的“雙向關係”,例如微信或者QQ中的“好友關係”。
- 在有向圖中,邊具有方向性,即𝐴 → 𝐵 和 𝐴 ← B兩個方向的邊是相互獨立的,例如微博或抖音上的“關注”與“被關注”關係。
根據所有頂點是否連通,可分為連通圖(connected graph)和非連通圖(disconnected graph)。
- 對於連通圖,從某個頂點出發,可以到達其餘任意頂點。
- 對於非連通圖,從某個頂點出發,至少有一個頂點無法到達。
我們還可以為邊新增“權重”變數,從而得到如圖所示的有權圖(weighted graph)。例如在《王者榮耀》等手遊中,系統會根據共同遊戲時間來計算玩家之間的“親密度”,這種親密度網路就可以使用有權圖來表示。
圖資料結構包含以下常用術語:
- 鄰接(adjacency):當兩個頂點之間存在邊相連時,稱這兩頂點“鄰接”。
- 路徑(path):從頂點A到頂點B經過的邊構成的序列被稱為從A到B的“路徑”。
- 度(degree):一個頂點擁有的邊數。對於有向圖,入度(in-degree)表示有多少條邊指向該頂點,出度(out-degree)表示有多少條邊從該頂點指出。
圖的表示
圖的常用表示方法包括“鄰接矩陣”和“鄰接表”。
鄰接矩陣
設圖的頂點數量為n,**鄰接矩陣(adjacency matrix)使用一個 n*n大小的矩陣來表示圖,每一行(列)代表一個頂點,矩陣元素代表邊,用1或0表示兩個頂點之間是否存在邊。
如下圖,設鄰接矩陣為M、頂點列表為V,那麼矩陣元素M[i,j] = 1表示頂點V[i]到頂點V[j]之間存在邊,反之M[i,j] = 0表示兩頂點之間無邊。
鄰接矩陣具有以下特性:
- 頂點不能與自身相連,因此鄰接矩陣主對角線元素沒有意義。
- 對於無向圖,兩個方向的邊等價,此時鄰接矩陣關於主對角線對稱。
- 將鄰接矩陣的元素從1和0替換為權重,則可表示有權圖。
使用鄰接矩陣表示圖時,我們可以直接訪問矩陣元素以獲取邊,因此增刪改查操作的效率很高,時間複雜度均為O(1)。然而矩陣的空間複雜度為O(n2),記憶體佔用較多。
鄰接表
鄰接表(adjacency list)使用n個連結串列來表示圖,連結串列節點表示頂點。第i個連結串列對應頂點i,其中儲存了該頂點的所有鄰接頂點(與該頂點相連的頂點)。
鄰接表進儲存實際存在的邊,而邊的總數通常遠小於n2,因此它更加節省空間。但是在鄰接表中需要透過遍歷連結串列來查詢邊,因此其查詢時間效率不如鄰接矩陣。
鄰接表結構與雜湊表中的“鏈式地址”非常相似,因此我們也可以採用類似方法來最佳化效率。比如當連結串列較長時,可以將連結串列轉化為AVL樹或紅黑樹,從而將時間效率從O(n)最佳化至O(logn);還可以把連結串列轉換為雜湊表,從而將時間複雜度降至O(1)。
圖的常見應用
許多現實系統可以用圖來建模,相應的問題也可以約化為圖計算問題。
頂點 | 邊 | 圖計算問題 | |
---|---|---|---|
社交網路 | 使用者 | 好友關係 | 潛在好友推薦 |
地鐵線路 | 站點 | 站點間的連通性 | 最短路線推薦 |
太陽系 | 星體 | 星體間的萬有引力作用 | 行星軌道計算 |