世間的一切對象都可化為節點;世間一切關係都可化為節點間的一條線;從而組成了如夢幻泡影的圖。將來的環球必定是圖的世界。
一、圖的表示
圖有有向圖和無向圖,表示方法一般有鄰接表、鄰接矩陣等方法,無向圖和有向圖都可以用這兩種方法表示。
圖1. 圖的例子[1]
1、鄰接表
在鄰接表中,對於每個頂點u,使用一個鏈表把所有與u相鄰的點點串起來,並標記這個集合為adj(u)。舉個栗子如下:
圖2. 鄰接表表示圖的例子[1]
在真正操作圖進行實驗的時候,一般也都使用鄰接矩陣表示,例如要儲存圖1中的有向圖,可以直接用一個csv或者txt檔案,儲存內容如下:
5,141,3,2,5,42,3,1,5,33,2,2,44,3,1,5,35,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.