世間的一切對象都可化為節點;世間一切關系都可化為節點間的一條線;從而組成了如夢幻泡影的圖。將來的環球必定是圖的世界。
一、圖的表示
圖有有向圖和無向圖,表示方法一般有鄰接表、鄰接矩陣等方法,無向圖和有向圖都可以用這兩種方法表示。
圖1. 圖的例子[1]
1、鄰接表
在鄰接表中,對於每個頂點u,使用一個鏈表把所有與u相鄰的點點串起來,並標記這個集合為adj(u)。舉個栗子如下:
圖2. 鄰接表表示圖的例子[1]
在真正操作圖進行實驗的時候,一般也都使用鄰接矩陣表示,例如要存儲圖1中的有向圖,可以直接用一個csv或者txt文件,存儲內容如下:
5,14
1,3,2,5,4
2,3,1,5,3
3,2,2,4
4,3,1,5,3
5,3,4,1,2
上述文件就是第一行存儲了總共的節點數量、邊的數量;接下來的每一行就是此頂點的id和鄰居的id,並且第2位數字存儲了該節點總共有多少鄰居節點;為了進一步的操作簡單,可以把原有的節點id映射為從0到n,沒有邊的節點用0補充,此時行號和id相等了,訪問起來更加迅速。這樣存儲的好處是需要較少的內存就可以完成,而且操作簡單。
2、鄰接矩陣
鄰接矩陣顧名思義就是用一個n*n的矩陣,存儲各節點之間的關系,空間復雜度\(O(n^2)\),這樣存儲在一些矩陣運算的時候較為方便,例如轉置。圖1中兩圖的鄰接矩陣如下:
圖3. 鄰接矩陣表示圖的例子[1]
二、圖周游算法
1、廣度優先遍歷
圖的廣度遍歷顧名思義就是訪問圖中節點的時候,優先在廣度上進行遍歷;也就是訪問到某節點A時,優先訪問完A的所有鄰居節點[節點的訪問順序並沒有做要求,在具體的問題中此處可以定義訪問順序],再繼續訪問鄰居的鄰居,直接看代碼:
#過程中主要是做三件事:顏色、距離、父節點
BFS(G(V,E),s)
#初始化
for each vertex u in V-{s}
color[u] <- White
d[u]<-∞
pai[u]<-None
endfor
#處理開始節點
color[s]<-Gray
d[s]<-0
pai[s]<-None
#初始化隊列
Q<-Queue()
Enqueue(Q,s)
#開始廣度訪問圖
while not Q.empyt():
u<-Dequeue(Q)
for each v in Adj[u]:
if color[v] = White #檢查顏色是否為白色,即沒有被訪問過
color[v] <- Gray #顏色,變顏色為灰色
d[v]<-d[u]+1 #距離
pai[v]<-u #父節點
endif
endfor
color[u]<-Black
endwhile
End
應用:
無向圖二着色問題,無奇回路<=>可以二着色<=>可以分為二部圖
2、深度優先遍歷
深度優先搜索是從某一頂點開始訪問,然后訪問他的鄰居,與BFS不同的是,當深度優先搜索從某個頂點u訪問他的鄰居時,只選擇其中的一個還未被訪問的鄰居v,然后暫時棄u的其他鄰居於不顧,而從新的兒子節點v去訪問v的鄰居。當v出發的訪問全部完成后,DFS回到u,然后再訪問u的第二個鄰居,以此類推。這里有個動畫就好了,可是我不會畫。
深度優先遍歷,迭代方法的偽代碼:
DFS(G(V,E))
#初始化訪問控制的顏色,父節點
for each vetex u in V
color[u] = White
pai[u]=None
endfor
#最開始,時間為0
time <- 0
#對於每個節點進行一次深度優先遍歷,防止圖節點間不連通
for each vetex u in V:
if color[u]=While:
then DFS-visit(u)
endif
endfor
End
#對節點s,進行深度優先遍歷
DFS-Visit(s):
color[s] <- Gray
time = time + 1
d[s]<-time
#如果訪問到節點s的鄰居節點,那么對其鄰居節點迭代進行深度優先遍歷
for each v in Adj[s]:
if color[v] = White
pai[v]<-s
DFS-Visit(v)
endif
endfor
color[s]<-Black
f[s]<-time<-time+1
End
2.1 區間套定理:
2.2 白路徑定理:
2.3 拓撲排序:
2.4 無回路有向圖中最長路徑問題:
一些應用問題需要找到圖中的最短或者最長的簡單路徑(不含回路的路徑),圖往往是加權的。但是對於任意圖,找一條最長路徑是NPC問題,即使這個圖是不加權的圖。然而只有這個圖是無回路的圖時,不論是有向圖還是無向圖,加權還是不加權圖,都可以在線性時間\(O(|V|+|E|)=O(n+m)\)。細心的我可能發現了,無回路的圖就是棵樹或者森林啊,兩個頂點之間要么不連通,要不然就只有唯一的一條路徑。
2.5 強連通分支:
定義1: 如果一個有向圖中任意一頂點都有一條通向其他任一頂點的路徑,那么這個有向圖稱為強連通圖(strongly connected graph)
定義2: 有向圖G,其隱含的無向圖\(G'\)是指把G中的每條邊的方向都去掉后所得到的無向圖。
定義3: 如果一個有向圖G所隱含的無向圖\(G'\)是個連通圖,那么有向圖G稱為弱連通圖(weakly connected graph)
定義4: 如果一個有向圖的子圖是個強連通圖,則成為強連通子圖(strongly connected subgraph)
定義5: 如果一個有向圖的強連通子圖已最大,即不能在加入其他任何一個頂點而仍然強連通,那么這個子圖稱為強連通分支(strongly connected component)
這里強連通分支包含於強連通子圖內。
定義6: 有向圖的強連通分支問題就是把一個有向圖的頂點划分為不相交的若干個強連通分支。
三、參考文獻
[1] 沈孝鈞. 計算機算法基礎 : Essentials of computer algorithms[M]. 機械工業出版社, 2014.